You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Introduce pointer to <script> element in module scripts #1013
There was an offline discussion about document.currentScript and shadow trees and the suggestion came up to solve it in a different way for module scripts so that the global object would not be involved and the value would only be accessible to the script currently executing.
I think this would belong in the "module meta" idea. It was detailed in more detail somewhere, but alluded to in whatwg/loader#38. @dherman might know more. The idea would be something like:
import{currentScript,url}fromthismodule;
At other times people have suggested using metaproperties on import (currently the only metaproperty in the language is new.target; this would introduce a few more):
import.meta.currentScript;// or just import.currentScript?
Unfortunately the base extensibility hook here kind of needs a TC39 proposal for someone to champion, which is a whole process.
Even if it were set in this case, I'd still want something similar to currentScript within imported modules. One use-case I have is to insert a template directly after the module script.
I like a variant of import.currentScript personally; I think introducing a special meta would invite questions like whether it is mutable or not. Maybe a better name came be thought of than currentScript (is it really current?) like ownerScript perhaps.
It's worth mentioning that we have a path forward here without waiting on TC39, which is to reserve a special module name. So then we'd do something like
import.currentScript sounds ideal here. With the dynamic import landed, perhaps a spec here can start to gain momentum. Landing features such as these soon will help significantly to avoid fragmentation of the adoption path features.
Note also feature detection here also seems tricky. I assume typeof import.currentScript wouldn't work, so it would just fall back to a syntax error if it isn't supported?
It's not directly related to this, I would like to note that I would like to have the similar thing in the module script in workers. See detailed thing why I would like to get it even in the workers. in tc39/proposal-dynamic-import#37.
@besworks The host document or the host node? 🤔 There's some stuff I could do with a host node to get some desired behavior at least. But not with a host document.
<div id="host"></div>
<script type="module">
let host = document.querySelector('#host');
let shadow = host.attachShadow({ mode:'closed' });
let script = document.createElement('script');
script.textContent = `console.log(document.querySelector('#host'))`;
shadow.append(script); console.log(script.ownerDocument);
</script>
You might expect that after the script was appended to the shadowRoot it would no longer be part of the host document. But this not the case. The shadowRoot is not actually a separate Document but rather a DocumentFragment contained as a property of the host element. We have full access to the host document scope and every element in it but no access to the scope the script is actually connected to, unless you drill down into it from outside, which you can't do if you use a closed shadowRoot.
So, even if you were to access the hypothetical import.meta.currentDocument from inside a shadowRoot, you would still get a reference to the document that contains your component and not the shadowRoot like you want unless special handling was put in place for this scenario. This may still prove to be the best route and less complex than messing with the ShadowRoot interface, though I do still believe there is room for improvement there.
One technique I experimented with involved building a virtual document to process my scripts in with the intention of appending the output to the shadowRoot afterwards. This didn't work out because according to section 8.1.3.4 of the spec
Scripting is disabled for a node when its node document browsing context is null.
Which is exactly the case when using document.implementation.createHTMLDocument() and DOMParser() and the same with new Document() ...
So, with all that in mind, the best workaround I've come up with, is the spooky dark magic method that always seems to ruffle everyone's feathers. But it gets the job done and, if used responsibly, overcomes all of these problems.
not sure if it's entirely related, but adding a use case here anyway.
i'm trying to write some isolated code examples for a documentation site. i wanted to use declarative shadow dom for this, so styles and IDs do not leak out. but i also wanted to include some scripts as part of demos e.g.
<divclass="example"><templateshadowrootmode="open"><style>button {
color: red;
}
</style><button>click me</button><script>// how to select the button?</script></template></div>
e.g. here i wanted to log a click on the button. but there's no easy way to get a reference to the button which doesn't completely detract from the example itself. it would be nice if there were something like currentDocument, though i am not precious about any particular solution.
Reacted by Gaurang Tandon, Jan Schjetne, Eugene, Souhail, Thomas Bonk, HE Shi-Jun and bg-churst
I know - this seems complicated but - trust me - i have use-cases, where this is the most robust way (dynamic generation of script tags, template engines, escaping, ide code handling, lsp support, and so on).
But with this pattern i am stuck to regular script types. I would really like to convert this to
Would having import.meta.scriptElement only defined for inline module scripts be a viable solution?
It seems to me that "what script element caused the loading of this module?" and/or "what script elements are connected to this module in the import graph?" are more complex and less useful questions to ask compared to "what script element contains this code?"
Conceptually, I think of <script type="module" src="foo.js"> as importing a module defined elsewhere, whereas <script type="module">console.log("hello, world")</script> defines a module in the current document. The definition for the former lives at a URL that can be introspected with import.meta.url. The definition for the latter lives in a HTMLScriptElement that can be introspected with import.meta.scriptElement.
Would having import.meta.scriptElement only defined for inline module scripts be a viable solution?
I think this makes some sense on the surface, but there's a big hazard with things changing so much depending on whether the script is inline. Tools may change a script from inline to external or vice-versa and break things.
You would get roughly the same effect if import.meta.scriptElement was defined only for the module directly imported by a script tag, and maybe only during the top-level evaluation of the module. Then if a script were made external, it would still work, since presumably it's also only imported once.
As @rniwa has pointed out, though, there isn't really a well-defined answer to "what script tag imported this module?" (nor, even, for "what module imported this module?") because a module can be imported from a bunch of different places at different times. That's why I think focusing on where the module is defined is a more well-founded approach.
The hazard of module behaviour changing depending on whether the script is inline applies to import.meta.url as well... I think that's just an unavoidable consequence of this kind of reflection. Presumably bundlers have to emulate import.meta.url too, right?
For external modules, this pattern might be reasonable:
The 1:N definition:import relationship has to get dealt with somewhere, but this way your something function can have whatever application-level semantics make sense.
The only other answer I can think of would be some kind of import hook that a module could use to know when it's been imported and what by? That seems like a whole can of worms though...
while I can sort of see why a script can't get a handle/reference to itself in a closed shadowroot, but what about an open shadowroot?
My trouble started with implementing 'Login With Google'... and using their script, which itself uses document.currentScript. It didn't just 'work'.
I had at first done some patching in the first case, and it ended up in a module, which then current script couldn't be used.... I was reimplementing it, and maybe that wasn't even the cause, but that it was in a shadow...
So now I have loaded the script into an open shadow (if it's open it's easy enough to iterate the page and get back into the shadow tree), and document.currentScript was null. - which it doesn't really have to be... there's nothing hidden or protected anyway.
I still don't understand how the HTML fragment getting its own information is 'leaked' information... even if it was a <script src= /> or even <script> .... </script> sort of element in a closed shadow fragment; why can't it know it exists? who else could run code containing 'document.currentScript' and get any information about THAT script ?
I was just using shadow for the encapsulation of styles; it's not really something I wanted to give a whole iframe sort of wrapping to.
those scripts don't run just once, but are the same script, no?
They have different URLs. Both URLs have to be downloaded independently. For both, the server can send whatever it likes. It may choose to send the same content for both (this is usually the case with simple static webspace) or send different content, or even act randomly.
Would having import.meta.scriptElement only defined for inline module scripts be a viable solution?
I would love to have currentScript for inline module script tags, so i can use import with surrealjs
Right now I am having to load modules, attach them to window, then send an event that tells the inline scripts that the module has been made available on window, because all module script tags are deferred until at least after all regular inline script tags have executed.
That's clever but I think it would harm what I was trying to do: have a basic isolated example of HTML/CSS/JS which is easy to follow. Tricks to grab an element would detract from that
Activity
domenic commentedon Apr 8, 2016
I think this would belong in the "module meta" idea. It was detailed in more detail somewhere, but alluded to in whatwg/loader#38. @dherman might know more. The idea would be something like:
At other times people have suggested using metaproperties on
import(currently the only metaproperty in the language isnew.target; this would introduce a few more):Unfortunately the base extensibility hook here kind of needs a TC39 proposal for someone to champion, which is a whole process.
Make module scripts modify less global script state
Make module scripts modify less global script state
Make module scripts modify less global script state
Don't update document.currentScript for module scripts
Execute <script> in shadow trees
Execute <script> in shadow trees
Execute <script> in shadow trees
Execute <script> in shadow trees
matthewp commentedon Oct 26, 2016
Is it correct to say that
document.currentScriptis not set, even for module scripts with no imports like:Even if it were set in this case, I'd still want something similar to currentScript within imported modules. One use-case I have is to insert a template directly after the module script.
I like a variant of
import.currentScriptpersonally; I think introducing a specialmetawould invite questions like whether it is mutable or not. Maybe a better name came be thought of than currentScript (is it really current?) like ownerScript perhaps.domenic commentedon Oct 26, 2016
That's correct.
It should really be current; I don't see why it wouldn't be. We're still kind of waiting on TC39 to figure out the import metaproperty stuff though.
domenic commentedon Nov 10, 2016
It's worth mentioning that we have a path forward here without waiting on TC39, which is to reserve a special module name. So then we'd do something like
guybedford commentedon Jan 12, 2017
import.currentScriptsounds ideal here. With the dynamic import landed, perhaps a spec here can start to gain momentum. Landing features such as these soon will help significantly to avoid fragmentation of the adoption path features.Note also feature detection here also seems tricky. I assume
typeof import.currentScriptwouldn't work, so it would just fall back to a syntax error if it isn't supported?Constellation commentedon Feb 9, 2017
It's not directly related to this, I would like to note that I would like to have the similar thing in the module script in workers. See detailed thing why I would like to get it even in the workers. in tc39/proposal-dynamic-import#37.
108 remaining items
whaaaley commentedon May 15, 2022
@besworks The host document or the host node? 🤔 There's some stuff I could do with a host node to get some desired behavior at least. But not with a host document.
besworks commentedon May 15, 2022
Here's an example of what I mean :
You might expect that after the script was appended to the shadowRoot it would no longer be part of the host document. But this not the case. The shadowRoot is not actually a separate Document but rather a DocumentFragment contained as a property of the host element. We have full access to the host document scope and every element in it but no access to the scope the script is actually connected to, unless you drill down into it from outside, which you can't do if you use a closed shadowRoot.
So, even if you were to access the hypothetical
import.meta.currentDocumentfrom inside a shadowRoot, you would still get a reference to the document that contains your component and not the shadowRoot like you want unless special handling was put in place for this scenario. This may still prove to be the best route and less complex than messing with the ShadowRoot interface, though I do still believe there is room for improvement there.One technique I experimented with involved building a virtual document to process my scripts in with the intention of appending the output to the shadowRoot afterwards. This didn't work out because according to section 8.1.3.4 of the spec
Which is exactly the case when using
document.implementation.createHTMLDocument()andDOMParser()and the same withnew Document()...So, with all that in mind, the best workaround I've come up with, is the spooky dark magic method that always seems to ruffle everyone's feathers. But it gets the job done and, if used responsibly, overcomes all of these problems.
mk-pmb commentedon May 16, 2022
I for one want to
scriptElement.getAttribute(…).WickyNilliams commentedon Mar 14, 2024
not sure if it's entirely related, but adding a use case here anyway.
i'm trying to write some isolated code examples for a documentation site. i wanted to use declarative shadow dom for this, so styles and IDs do not leak out. but i also wanted to include some scripts as part of demos e.g.
e.g. here i wanted to log a click on the button. but there's no easy way to get a reference to the button which doesn't completely detract from the example itself. it would be nice if there were something like
currentDocument, though i am not precious about any particular solution.MajPay commentedon Oct 11, 2024
One of my favourite patterns (because its very robust) is to do the following:
I know - this seems complicated but - trust me - i have use-cases, where this is the most robust way (dynamic generation of script tags, template engines, escaping, ide code handling, lsp support, and so on).
But with this pattern i am stuck to regular script types. I would really like to convert this to
sgentle commentedon Nov 8, 2024
Would having
import.meta.scriptElementonly defined for inline module scripts be a viable solution?It seems to me that "what script element caused the loading of this module?" and/or "what script elements are connected to this module in the import graph?" are more complex and less useful questions to ask compared to "what script element contains this code?"
Conceptually, I think of
<script type="module" src="foo.js">as importing a module defined elsewhere, whereas<script type="module">console.log("hello, world")</script>defines a module in the current document. The definition for the former lives at a URL that can be introspected withimport.meta.url. The definition for the latter lives in aHTMLScriptElementthat can be introspected withimport.meta.scriptElement.Does that make sense?
justinfagnani commentedon Nov 9, 2024
I think this makes some sense on the surface, but there's a big hazard with things changing so much depending on whether the script is inline. Tools may change a script from inline to external or vice-versa and break things.
You would get roughly the same effect if
import.meta.scriptElementwas defined only for the module directly imported by a script tag, and maybe only during the top-level evaluation of the module. Then if a script were made external, it would still work, since presumably it's also only imported once.sgentle commentedon Nov 9, 2024
As @rniwa has pointed out, though, there isn't really a well-defined answer to "what script tag imported this module?" (nor, even, for "what module imported this module?") because a module can be imported from a bunch of different places at different times. That's why I think focusing on where the module is defined is a more well-founded approach.
The hazard of module behaviour changing depending on whether the script is inline applies to
import.meta.urlas well... I think that's just an unavoidable consequence of this kind of reflection. Presumably bundlers have to emulateimport.meta.urltoo, right?For external modules, this pattern might be reasonable:
The 1:N definition:import relationship has to get dealt with somewhere, but this way your
somethingfunction can have whatever application-level semantics make sense.The only other answer I can think of would be some kind of import hook that a module could use to know when it's been imported and what by? That seems like a whole can of worms though...
mk-pmb commentedon Nov 9, 2024
I could imagine a world in which the script tag has a getter for the exports of the imported module and we could use
Maybe we can even convince browsers to support a
runshorthand for a method name exposed on the default export, so we could shorten the above to:Or when it's empty, it means that the default export is expected to be the entrypoint function, i.e.
would be the same as
I think the four bytes " run" could become one of webdev's most beloved HTML additions. (In before "you can even omit the space!")
d3x0r commentedon Jan 4, 2025
while I can sort of see why a script can't get a handle/reference to itself in a closed shadowroot, but what about an open shadowroot?
My trouble started with implementing 'Login With Google'... and using their script, which itself uses document.currentScript. It didn't just 'work'.
I had at first done some patching in the first case, and it ended up in a module, which then current script couldn't be used.... I was reimplementing it, and maybe that wasn't even the cause, but that it was in a shadow...
So now I have loaded the script into an open shadow (if it's open it's easy enough to iterate the page and get back into the shadow tree), and
document.currentScriptwas null. - which it doesn't really have to be... there's nothing hidden or protected anyway.I still don't understand how the HTML fragment getting its own information is 'leaked' information... even if it was a
<script src= />or even<script> .... </script>sort of element in a closed shadow fragment; why can't it know it exists? who else could run code containing 'document.currentScript' and get any information about THAT script ?I was just using shadow for the encapsulation of styles; it's not really something I wanted to give a whole iframe sort of wrapping to.
Going back though the comments
those scripts don't run just once, but are the same script, no?
mk-pmb commentedon Jan 5, 2025
They have different URLs. Both URLs have to be downloaded independently. For both, the server can send whatever it likes. It may choose to send the same content for both (this is usually the case with simple static webspace) or send different content, or even act randomly.
quinn commentedon Jan 23, 2025
I would love to have currentScript for inline module script tags, so i can use import with surrealjs
Right now I am having to load modules, attach them to window, then send an event that tells the inline scripts that the module has been made available on window, because all module script tags are deferred until at least after all regular inline script tags have executed.
ioquatix commentedon Mar 14, 2025
Is there an official standard for this feature?
akvadrako commentedon Jul 14, 2026
@WickyNilliams
In this case you can just use
<button onclick="handler(this)">.Oh if you don't want a click handler you can try:
<svg onload="window.myNode = this.parentNode"></svg>.WickyNilliams commentedon Jul 14, 2026
That's clever but I think it would harm what I was trying to do: have a basic isolated example of HTML/CSS/JS which is easy to follow. Tricks to grab an element would detract from that