382

How can I reference the script element that loaded the javascript that is currently running?

Here's the situation. I have a "master" script being loaded high in the page, first thing under the HEAD tag.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="scripts.js"></script>

There is a script in "scripts.js" which needs to be able to do on-demand loading of other scripts. The normal method doesn't quite work for me because I need to add new scripts without referencing the HEAD tag, because the HEAD element hasn't finished rendering:

document.getElementsByTagName('head')[0].appendChild(v);

What I want to do is reference the script element that loaded the current script so that I can then append my new dynamically loaded script tags into the DOM after it.

<script type="text/javascript" src="scripts.js"></script>
loaded by scripts.js--><script type="text/javascript" src="new_script1.js"></script>
loaded by scripts.js --><script type="text/javascript" src="new_script2.js"></script>
Shog9
  • 156,901
  • 35
  • 231
  • 235
  • 8
    Looks like it's up on caniuse now: http://caniuse.com/#feat=document-currentscript – Tyler Jan 26 '17 at 16:26
  • 1
    A word of warning: modifying the DOM while it is still loading will cause you a [world of hurt in IE6 & IE7](http://support.microsoft.com/kb/927917/en-us). You're gonna be better off running that code after page load. – Kenan Banks Dec 31 '08 at 20:32

14 Answers14

829

How to get the current script element:

1. Use document.currentScript

document.currentScript will return the <script> element whose script is currently being processed.

<script>
var me = document.currentScript;
</script>

Benefits

  • Simple and explicit. Reliable.
  • Don't need to modify the script tag
  • Works with asynchronous scripts (defer & async)
  • Works with scripts inserted dynamically

Problems

  • Will not work in older browsers and IE.
  • Does not work with modules <script type="module">

2. Select script by id

Giving the script an id attribute will let you easily select it by id from within using document.getElementById().

<script id="myscript">
var me = document.getElementById('myscript');
</script>

Benefits

  • Simple and explicit. Reliable.
  • Almost universally supported
  • Works with asynchronous scripts (defer & async)
  • Works with scripts inserted dynamically

Problems

  • Requires adding a custom attribute to the script tag
  • id attribute may cause weird behaviour for scripts in some browsers for some edge cases

3. Select the script using a data-* attribute

Giving the script a data-* attribute will let you easily select it from within.

<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>

This has few benefits over the previous option.

Benefits

  • Simple and explicit.
  • Works with asynchronous scripts (defer & async)
  • Works with scripts inserted dynamically

Problems

  • Requires adding a custom attribute to the script tag
  • HTML5, and querySelector() not compliant in all browsers
  • Less widely supported than using the id attribute
  • Will get around <script> with id edge cases.
  • May get confused if another element has the same data attribute and value on the page.

4. Select the script by src

Instead of using the data attributes, you can use the selector to choose the script by source:

<script src="//example.com/embed.js"></script>

In embed.js:

var me = document.querySelector('script[src="//example.com/embed.js"]');

Benefits

  • Reliable
  • Works with asynchronous scripts (defer & async)
  • Works with scripts inserted dynamically
  • No custom attributes or id needed

Problems

  • Does not work for local scripts
  • Will cause problems in different environments, like Development and Production
  • Static and fragile. Changing the location of the script file will require modifying the script
  • Less widely supported than using the id attribute
  • Will cause problems if you load the same script twice

5. Loop over all scripts to find the one you want

We can also loop over every script element and check each individually to select the one we want:

<script>
var me = null;
var scripts = document.getElementsByTagName("script")
for (var i = 0; i < scripts.length; ++i) {
    if( isMe(scripts[i])){
      me = scripts[i];
    }
}
</script>

This lets us use both previous techniques in older browsers that don't support querySelector() well with attributes. For example:

function isMe(scriptElem){
    return scriptElem.getAttribute('src') === "//example.com/embed.js";
}

This inherits the benefits and problems of whatever approach is taken, but does not rely on querySelector() so will work in older browsers.

6. Get the last executed script

Since the scripts are executed sequentially, the last script element will very often be the currently running script:

<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>

Benefits

  • Simple.
  • Almost universally supported
  • No custom attributes or id needed

Problems

  • Does not work with asynchronous scripts (defer & async)
  • Does not work with scripts inserted dynamically
Flimm
  • 136,138
  • 45
  • 251
  • 267
brice
  • 24,329
  • 7
  • 79
  • 95
  • 2
    Agree with @RoyiNamir. This is the best answer. – Hans May 23 '14 at 18:09
  • 33
    Thanks guys, but you know I answered 4 *years* after the accepted answer, right :) – brice May 25 '14 at 00:26
  • @Josh are you trying it on the console? It's expected to be null in that case. Otherwise, that's a bug against Waterfox. Try this test page: http://fractallambda.com/misc/testDocumentCurrentScript/ – brice Jun 27 '14 at 21:14
  • If you're willing to ditch IE support, you can probably express #6 even more succinctly as `document.querySelector('script:last-child').remove()` – Nuck Sep 22 '14 at 06:41
  • This is a great answer. I would like to point out that according to the HTML5 standard, id is a global attribute and may be specified on all HTML elements, including – Christopher Weiss Oct 05 '14 at 01:43
  • Great answer... should be answer... and StackOverflow should have a mechanism to mark the best answer... right in top of page (a star flashing with the title), so we can click on it and scrool to the answer that were marked by several users as the best!!! – ZEE Oct 06 '14 at 20:12
  • @ChristopherWeiss I stand corrected! Can't remember where I read that `id` attributes would be problematic on scripts inside the ``. Probably a renderer-specific issue, rather than spec issue. – brice Oct 06 '14 at 20:15
  • 6
    "document.currentScript" doesn't work for me with dynamic loaded scripts, returns null in the latest chrome/firefox, "last executed script" works ok – xwild Feb 12 '16 at 04:15
  • 3
    doesn't work when `script` is in a `template` inserted in a Shadow DOM – Supersharp Mar 22 '17 at 16:07
  • Which option should I use? I'd like to pass some parameters with data-* attributes to the script. Now I'm wondering if this is a good idea. Is there not jQuery option that does the magic? – robsch Sep 19 '17 at 06:29
  • 2
    Full respect to @brice for being humble with his comment about 4 years after accepted answer (I make this comment _9_ years after post and have no idea where that accepted answer has gone...). This is a superb answer and helped me considerably. _This_ is the SO community that makes it work so well - producers re-answering old posts with these type of answers benefit consumers like myself who stumble upon these archaic posts and glean valuable nuggets. Thank you kind sir! – Psyrus Nov 05 '17 at 00:38
  • 1
    As of 2019-Oct, document.currentScript still returns null in latest Chrome. – den232 Oct 15 '19 at 14:19
  • 1
    @den232 - Are you trying this in the console? This is expected to return null. Try it by embedding the script on a test page to get the right behaviour in chrome. Try this test page: http://fractallambda.com/misc/testDocumentCurrentScript/ – brice Oct 16 '19 at 17:05
  • @brice - Of course. You are sooo right. my duh. Thanks jb – den232 Oct 17 '19 at 18:51
  • document.currentScript returns null in a shadow DOM in Chrome today.' – Leo Feb 19 '20 at 00:53
  • For those using option #2 with jQuery, note that $("#myscript") does not work while the script is executing but you can use $(document.getElementById('myscript')) – user2880616 Apr 06 '20 at 15:02
  • If it's `type="module"`, `import.meta.url` can make "select by src" approach more reliable. However, it still suffer the problem when a file.js src in many – DrSensor Nov 11 '21 at 20:57
90

Since scripts are executed sequentially, the currently executed script tag is always the last script tag on the page until then. So, to get the script tag, you can do:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
Coffee Bite
  • 4,956
  • 5
  • 33
  • 38
  • 3
    This is simple and elegant. There's an example of it in the new Google Charts/Visualizations API if you unpack the javascript. They load JSON data from within the script tag, see: http://ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js – Jason Thrasher Nov 17 '10 at 23:13
  • 2
    This is a great idea, and it normally works for me. But I should add that there are times when I've found it returning a reference to a different script. Not sure why - haven't been able to track that down. Consequently, I usually go with a different method, e.g., I hard-code the name of the script file, and look for the script tag with that file name. – Ken Smith Nov 18 '11 at 22:28
  • 55
    One instance that I can think of where this might return incorrect results is when a script tag is added to the DOM asynchronously. – Coffee Bite Nov 25 '11 at 14:41
  • 9
    Yes this can have unpredictable results so you could try using a selector instead: $('script[src*="/mysource.js"]') ??? – King Friday Nov 27 '12 at 21:52
  • 7
    It doesn't work when you have scripts loaded after page loaded. You probably won't get the right tag. – ThemeZ Feb 22 '13 at 06:32
  • How to make it work when the script loads in an async way? I am looking to get the GET parameters. – neeagl Oct 16 '14 at 19:17
  • Note this thing works if you query for self **directly in the script main scope**. If you do it in a method, other scripts may get loaded in the meantime and distort this. – jakub.g May 04 '15 at 15:40
  • Since Javascript is often event based then any of the scripts in the array have an equal chance of being the currently executing script, don't they? – Spencer O'Reilly Jan 25 '18 at 20:10
  • I have found that this does not always work. It is better to add a reverse find script instead. var scripts = document.getElementsByTagName('script'); var thisScript = null; var i = scripts.length; while (i--) { if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) { thisScript = scripts[i]; break; } } – Travis Tidwell Aug 20 '18 at 18:17
  • WARNING: this is true only if you don't use asynch or defer – Luca C. Jul 12 '19 at 09:44
17

Probably the easiest thing to do would be to give your scrip tag an id attribute.

Greg
  • 316,276
  • 54
  • 369
  • 333
  • 3
    Although you are right, there are lot's of cases in which the OP's question are valid, a couple would be: 1) when you're crawling 2) when you are working with the DOM of a client, and he is unwilling to change – nichochar Sep 08 '15 at 23:42
12

Here's a bit of a polyfill that leverages document.CurrentScript if it exists and falls back to finding the script by ID.

<script id="uniqueScriptId">
    (function () {
        var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');

        // your code referencing thisScript here
    ());
</script>

If you include this at the top of every script tag I believe you'll be able to consistently know which script tag is being fired, and you'll also be able to reference the script tag in the context of an asynchronous callback.

Untested, so leave feedback for others if you try it.

brice
  • 24,329
  • 7
  • 79
  • 95
Art Lawry
  • 121
  • 1
  • 2
  • The `id` attribute is [invalid](https://www.tutorialspoint.com/html/html_attributes_reference.htm) in a `script` element though. What sorts of problems could this approach generate? – n.r. Jan 19 '18 at 17:14
  • 2
    @n.r. - No, **all** elements can have an `id` attribute. `id`, `class`, and `slot` are defined at the DOM level, not the HTML level. If you go to the [global attributes in HTML](https://html.spec.whatwg.org/multipage/dom.html#global-attributes) and scroll past the list, you'll find *"DOM standard defines the user agent requirements for the class, id, and slot attributes for any element in any namespace."* followed by *"The class, id, and slot attributes may be specified on all HTML elements."* The DOM spec covers it [here](https://html.spec.whatwg.org/multipage/dom.html#global-attributes). – T.J. Crowder Jun 25 '19 at 10:34
12

Script are executed sequentially only if they do not have either a "defer" or an "async" attribute. Knowing one of the possible ID/SRC/TITLE attributes of the script tag could work also in those cases. So both Greg and Justin suggestions are correct.

There is already a proposal for a document.currentScript on the WHATWG lists.

EDIT: Firefox > 4 already implement this very useful property but it is not available in IE11 last I checked and only available in Chrome 29 and Safari 8.

EDIT: Nobody mentioned the "document.scripts" collection but I believe that the following may be a good cross browser alternative to get the currently running script:

var me = document.scripts[document.scripts.length -1];
ExceptionLimeCat
  • 6,191
  • 6
  • 44
  • 77
Diego Perini
  • 8,003
  • 1
  • 18
  • 9
  • 1
    It's document.scripts not document.script – Moritz Jun 09 '17 at 11:47
  • `document.scripts` didn't exist in FF4 we discovered when some tests failed. So you are safer using `document.getElementsByTagName('script')` if you want old browser support. – mattpr Sep 28 '22 at 13:25
8

It must works at page load and when an script tag is added with javascript (ex. with ajax)

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
HoLyVieR
  • 10,985
  • 5
  • 42
  • 67
6

To get the script, that currently loaded the script you can use

var thisScript = document.currentScript;

You need to keep a reference at the beginning of your script, so you can call later

var url = thisScript.src
5

An approach for dealing with async & deferred scripts is to leverage the onload handler- set an onload handler for all script tags and the first one which executes should be yours.

function getCurrentScript(callback) {
  if (document.currentScript) {
    callback(document.currentScript);
    return;
  }
  var scripts = document.scripts;
  function onLoad() {
    for (var i = 0; i < scripts.length; ++i) {
      scripts[i].removeEventListener('load', onLoad, false);
    }
    callback(event.target);
  }
  for (var i = 0; i < scripts.length; ++i) {
    scripts[i].addEventListener('load', onLoad, false);
  }
}

getCurrentScript(function(currentScript) {
  window.console.log(currentScript.src);
});
Pete Blois
  • 214
  • 3
  • 4
4

Follow these simple steps to obtain reference to current executing script block:

  1. Put some random unique string within the script block (must be unique / different in each script block)
  2. Iterate result of document.getElementsByTagName('script'), looking the unique string from each of their content (obtained from innerText/textContent property).

Example (ABCDE345678 is the unique ID):

<script type="text/javascript">
var A=document.getElementsByTagName('script'),i=count(A),thi$;
for(;i;thi$=A[--i])
  if((thi$.innerText||thi$.textContent).indexOf('ABCDE345678'))break;
// Now thi$ is refer to current script block
</script>

btw, for your case, you can simply use old fashioned document.write() method to include another script. As you mentioned that DOM is not rendered yet, you can take advantage from the fact that browser always execute script in linear sequence (except for deferred one that will be rendered later), so the rest of your document is still "not exists". Anything you write through document.write() will be placed right after the caller script.

Example of original HTML page:

<!doctype html>
<html><head>
<script src="script.js"></script>
<script src="otherscript.js"></script>
<body>anything</body></html>

Content of script.js:

document.write('<script src="inserted.js"></script>');

After rendered, the DOM structure will become:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY
Ferdinand Liu
  • 314
  • 1
  • 3
  • This seems to work only for inline scripts, not for external scripts. In the latter case all properties innerText, text, and textContent are empty. – Jos de Jong May 06 '14 at 08:55
3

Consider this algorithm. When your script loads (if there are multiple identical scripts), look through document.scripts, find the first script with the correct "src" attribute, and save it and mark it as 'visited' with a data-attribute or unique className.

When the next script loads, scan through document.scripts again, passing over any script already marked as visited. Take the first unvisited instance of that script.

This assumes that identical scripts will likely execute in the order in which they are loaded, from head to body, from top to bottom, from synchronous to asynchronous.

(function () {
  var scripts = document.scripts;

  // Scan for this data-* attribute
  var dataAttr = 'data-your-attribute-here';

  var i = 0;
  var script;
  while (i < scripts.length) {
    script = scripts[i];
    if (/your_script_here\.js/i.test(script.src)
        && !script.hasAttribute(dataAttr)) {

        // A good match will break the loop before
        // script is set to null.
        break;
    }

    // If we exit the loop through a while condition failure,
    // a check for null will reveal there are no matches.
    script = null;
    ++i;
  }

  /**
   * This specific your_script_here.js script tag.
   * @type {Element|Node}
   */
  var yourScriptVariable = null;

  // Mark the script an pass it on.
  if (script) {
    script.setAttribute(dataAttr, '');
    yourScriptVariable = script;
  }
})();

This will scan through all the script for the first matching script that isn't marked with the special attribute.

Then mark that node, if found, with a data-attribute so subsequent scans won't choose it. This is similar to graph traversal BFS and DFS algorithms where nodes may be marked as 'visited' to prevent revisitng.

LSOJ
  • 51
  • 5
1

I've got this, which is working in FF3, IE6 & 7. The methods in the on-demand loaded scripts aren't available until page load is complete, but this is still very useful.

//handle on-demand loading of javascripts
makescript = function(url){
    var v = document.createElement('script');
    v.src=url;
    v.type='text/javascript';

    //insertAfter. Get last <script> tag in DOM
    d=document.getElementsByTagName('script')[(document.getElementsByTagName('script').length-1)];
    d.parentNode.insertBefore( v, d.nextSibling );
}
1

I was inserting script tags dynamically with this usual alternative to eval and simply set a global property currentComponentScript right before adding to the DOM.

  const old = el.querySelector("script")[0];
  const replacement = document.createElement("script");
  replacement.setAttribute("type", "module");
  replacement.appendChild(document.createTextNode(old.innerHTML));
  window.currentComponentScript = replacement;
  old.replaceWith(replacement);

Doesn't work in a loop though. The DOM doesn't run the scripts until the next macrotask so a batch of them will only see the last value set. You'd have to setTimeout the whole paragraph, and then setTimeout the next one after the previous finishes. I.e. chain the setTimeouts, not just call setTimeout multiple times in a row from a loop.

Ron Newcomb
  • 2,886
  • 21
  • 24
0

If you can assume the file name of the script, you can find it. I've only really tested the following function in Firefox so far.

  function findMe(tag, attr, file) {
    var tags = document.getElementsByTagName(tag);
    var r = new RegExp(file + '$');
    for (var i = 0;i < tags.length;i++) {
      if (r.exec(tags[i][attr])) {
        return tags[i][attr];
      }
    }
  };
  var element = findMe('script', 'src', 'scripts.js');
Justin Love
  • 4,397
  • 25
  • 36
-1

I have found the following code to be the most consistent, performant, and simple.

var scripts = document.getElementsByTagName('script');
var thisScript = null;
var i = scripts.length;
while (i--) {
  if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) {
    thisScript = scripts[i];
    break;
  }
}
console.log(thisScript);
Travis Tidwell
  • 5,360
  • 1
  • 14
  • 9