Joel Regus
Photo by Ryan Stone on Unsplash

How to Get a Reference to the Currently Executing Script in JavaScript

By Joel Regus on February 05, 2021 · 7 min read
Programming JavaScript

This guide will show you how to get a reference to the script element that is currently executing JavaScript code

Introduction

Why Would You Even Want to Do This?

  1. You want to pass data to the code that you just loaded, and you decided to place that data directly on the <script> element.
    • This could be done via data attributes, key value pairs within the <script src>'s query string, or it could be placed directly on the JavaScript object that references the <script>
  2. You need to traverse the DOM relative to where your <script> was loaded.
  3. You want to know if the code that is currently running was loaded via async or defer or some other attribute of the <script>.

Why Is This So Difficult?

In the old days there was no link between the code that was currently running and the <script> that loaded it. There could be hundreds of <script> elements on the page, how do we know which is the one that is currently running our code?

Modern Browsers

document.currentScript

According to MDN:

The Document.currentScript property returns the <script> element whose script is currently being processed and isn't a JavaScript module.

Life is good, all we have to do to run this:

var curScriptElement = document.currentScript;

Caveats

document.currentScript doesn't work for Modules, Shadow DOM or within event handlers and timers.

If you need to do this in a module try using import.meta.

Unfortunately document.currentScript does not exist in older browsers.

Legacy Browsers

script.readyState

Internet Explorer exposes a property on script elements that tells you the script's current state. When the state is equal to "interactive" then you will know that that this is the currently executing script.

var scripts = document.getElementsByTagName("script");
var script;

for (var i = scripts.length - 1; i > -1; i--) {
  if (scripts[i].readyState === "interactive") {
    script = scripts[i];
    break;
  }
}

Caveat

This only exists in Internet Explorer 10 and under, they decided to remove this in IE11.

Compare Script URLs

If you know the URL of your <script> then you can look for that string among all of the other <script> tags.

var scripts = document.getElementsByTagName("script");
var url = "http://www.example.com";
var script;

for (var i = scripts.length - 1; i > -1; i--) {
  if (scripts[i].src === url) {
    script = scripts[i];
    break;
  }
}

Caveats

What if You Don't Know Your Script's URL?

Then you will need make an error object and use a regex to extract the URL of the current script from the stack trace.

What if You didn't Set a src Value?

If there is no URL to compare then you will need to compare the text content of your script against all the other script tags on the page. I would suggest wrapping your code in a function and stringify it by using the wrapper function's toString method.

You can get the text content of a script tag by using its text, textContent or innerHTML properties. You will probably want to use .trim() on the strings to get rid of whitespace.

What If There Are Multiple Instances of Your Script on the Page and They All Have the Same URL?

If your script was inserted directly into the page via the original HTML then you shouldn't have a problem. The browser will block everything while it downloads and parses your script. You should find your script if you iterate over the HTMLCollection returned by document.getElementsByTagName in reverse.

What if the Scripts Were Loaded Dynamically?

If you load several scripts dynamically using JavaScript then the browser will not block. This will cause your scripts to load out of order and break the behavior listed above. You can force the browser to block while loading the script by setting async to false.

var script = document.createElement("script");
script.async = false;
script.src = "http://example.com/script.js";
document.body.appendChild(script);

This trick doesn't work in IE9.

Add an ID to the Script

If you add an id to the script tag then you can easily retrieve it using document.getElementById.

<script id="foo">
  var script = document.getElementById("foo");
</script>

You are not limited to only using an id, you could also use document.querySelector to query for whatever attribute on the script tag you want, even the URL.

Caveats

What if I Want to Load an External Script?

Then you will need to know what the id will be ahead of time and hard code it in your script.

What If I Don't Know the ID Ahead of Time?

You can dynamically generate your script on the server and have the client tell you what the id will be. This will also help you if you need to have multiple versions of the same script on a page, but all this work might not be worth the effort.

What if I Don't Want to Do This on the Server?

You will need to add a placeholder value for the id in the script you intend to load.

// Inside the script;
var script = document.getElementById("__REPLACE_ME__");

You can load your script using XMLHttpRequest. Once it loads store the response string in a variable.

You can then replace the placeholder with the actual value of the id.

Create a script element, set its text parameter to the string you just modified.

Once you add this script to the body it should be able to find your script.


<script>
    var req = new XMLHttpRequest();

    req.addEventListener("load", function(e) {
        var code = e.target.responseText;

        var id = "foo";
        code = code.replace("__REPLACE_ME__", id);

        var script = document.createElement("script");
        
        script.text = code;
        script.id = id;
        
        document.body.appendChild(script);
    });

    req.open("GET", "http://example.com/script.js");
    req.send();
</script>

This approach is likely to have issue with performance, and you are going to have trouble loading cross origin files.

Load the Script in an Iframe

If you create an iframe and load your script in there then you will easily be able to find the script because it will be the only script tag within the iframe.

<script>
  var script = document.createElement("script");
  script.src = "http://example.com/script.js";

  var iframe = document.createElement("iframe");
  document.body.appendChild(iframe);

  iframe.contentDocument.appendChild(script);
</script>

// Inside the loaded script
var script = document.getElementsByTagName("script")[0];

Caveats

Loading an iframe is an expensive operation.

The onload Event

The load event will fire after the the <script> has been loaded, parsed, and executed. The event handler will have a reference to the <script> element. If the code you load places a callback on the window then the load event handler can then execute your callback and pass the <script> reference into your function.

<script>
  var script = document.createElement("script");
  script.src = "http://example.com/script.js";

  script.onload = function() {
    // "this" is the script element
    window.callbackFunction(this);
  };

  document.body.appendChild(script);
</script>

// Inside the script
window.callbackFunction = function(script) {
	// We now have a reference to the script
}

At this point though this isn't really the currently executing script, but the most recently executed script.

Caveats

You might run into a few problems here:

  1. There could be namespace collisions when adding the callback to the window, either from third-party scripts or from yourself if you load multiple instances of the same tag
  2. A third-party script might call your callback. You might want to remove it from the window once you get what you need
  3. If you need to perform an action immediately doing this might add some delay
  4. There is no guarantee that the event will fire immediately after the script executes, something weird might happen while you are waiting (DOM changes, scripts loading, etc).
  5. This technique is not as easy to reason about than the others

Conclusion

document.currentScript is amazing, look at how much work this one simple API saves us from having to do. This is something that I think should have been around since day one, but it first appeared in Chrome in version 29 which was in 2013.

Unfortunately for me I am currently working on a project where I need to solve this problem in older browsers, and my weird use case requires me to use some of these hacky work arounds.

Do you know of any other ways to solve this problem? If so then please send me an email!

Also, sorry if I confused you by using the word script interchangeably to mean <script> tag and JavaScript code.

Want to contact me?

Feel free to email me if you would like to get in touch.