🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

Introduce pointer to <script> element in module scripts #1013

Open

Description

@annevk

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.

Activity

  1. domenic commented on Apr 8, 2016

    @domenic
    Member

    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 } from this module;

    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.

  2. added a commit that references this issue on Apr 22, 2016
  3. added a commit that references this issue on Apr 22, 2016
  4. added a commit that references this issue on Apr 23, 2016
  5. added a commit that references this issue on Apr 26, 2016
  6. matthewp commented on Oct 26, 2016

    @matthewp

    Is it correct to say that document.currentScript is not set, even for module scripts with no imports like:

    <script type="module">
      document.currentScript == null;
    </script>

    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.

  7. domenic commented on Oct 26, 2016

    @domenic
    Member

    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.

  8. domenic commented on Nov 10, 2016

    @domenic
    Member

    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, url } from "js:context";
  9. guybedford commented on Jan 12, 2017

    @guybedford
    Contributor

    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?

  10. Constellation commented on Feb 9, 2017

    @Constellation
    Member

    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.

  11. 108 remaining items

  12. whaaaley commented on May 15, 2022

    @whaaaley

    @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.

  13. besworks commented on May 15, 2022

    @besworks

    Here's an example of what I mean :

    <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.

  14. mk-pmb commented on May 16, 2022

    @mk-pmb

    It seems to me the primary reason for wanting scriptElement is just so we can traverse upwards

    I for one want to scriptElement.getAttribute(…).

  15. WickyNilliams commented on Mar 14, 2024

    @WickyNilliams

    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.

    <div class="example">
      <template shadowrootmode="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.

  16. MajPay commented on Oct 11, 2024

    @MajPay

    One of my favourite patterns (because its very robust) is to do the following:

    <script data-foo="someOptionAddedByServerSideTemplatingEngine">
    (function(options){
      console.log(options.foo);
    })(document.currentScript.dataset);
    </script>

    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

    <script type="module" data-foo="someOptionAddedByServerSideTemplatingEngine">
    const options = import.meta.currentScript.dataset;
    console.log(options);
    </script>
  17. sgentle commented on Nov 8, 2024

    @sgentle

    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.

    Does that make sense?

  18. justinfagnani commented on Nov 9, 2024

    @justinfagnani

    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.

  19. sgentle commented on Nov 9, 2024

    @sgentle

    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:

    <script type="module">
    import something from 'module.js'
    something(import.meta.scriptElement)
    </script>

    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...

  20. mk-pmb commented on Nov 9, 2024

    @mk-pmb

    I could imagine a world in which the script tag has a getter for the exports of the imported module and we could use

    <script type="module" src="http://example.net/opaque.js" onload="
      const scriptTag = this;
      scriptTag.getExport('default').then(m => m.install(scriptTag));
    ">/* … arbitrary data, e.g. config settings … */</script>

    Maybe we can even convince browsers to support a run shorthand for a method name exposed on the default export, so we could shorten the above to:

    <script type="module" src="http://example.net/opaque.js" run="install">/* …data… */</script>

    Or when it's empty, it means that the default export is expected to be the entrypoint function, i.e.

    <script type="module" src="http://example.net/opaque.js" run>/* …data… */</script>

    would be the same as

    <script type="module" src="http://example.net/opaque.js" onload="
      const scriptTag = this;
      scriptTag.getExport('default').then(m => m(scriptTag));
    ">/* …data… */</script>

    I think the four bytes " run" could become one of webdev's most beloved HTML additions. (In before "you can even omit the space!")

  21. d3x0r commented on Jan 4, 2025

    @d3x0r

    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.


    Going back though the comments

    import {a as one} from "a.js";
    import (a as two} from "a.js?1";

    those scripts don't run just once, but are the same script, no?

  22. mk-pmb commented on Jan 5, 2025

    @mk-pmb

    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.

  23. quinn commented on Jan 23, 2025

    @quinn

    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.

  24. ioquatix commented on Mar 14, 2025

    @ioquatix

    Is there an official standard for this feature?

  25. akvadrako commented on Jul 14, 2026

    @akvadrako

    @WickyNilliams

    <button>click me</button>
    <script>
      // how to select the button?
    </script>
    

    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>.

  26. WickyNilliams commented on Jul 14, 2026

    @WickyNilliams

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions