How may I reference the script tag that loaded the currently-executing script?

JavascriptElementParent

Javascript Problem Overview


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>

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

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 ];

Solution 3 - Javascript

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

Solution 4 - Javascript

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];

Solution 5 - Javascript

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.

Solution 6 - Javascript

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>

Solution 7 - Javascript

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

Solution 8 - Javascript

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

Solution 9 - Javascript

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);
});

Solution 10 - Javascript

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.

Solution 11 - Javascript

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 );
}

Solution 12 - Javascript

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.

Solution 13 - Javascript

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');

Solution 14 - Javascript

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);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongeuisView Question on Stackoverflow
Solution 1 - JavascriptbriceView Answer on Stackoverflow
Solution 2 - JavascriptCoffee BiteView Answer on Stackoverflow
Solution 3 - JavascriptGregView Answer on Stackoverflow
Solution 4 - JavascriptDiego PeriniView Answer on Stackoverflow
Solution 5 - JavascriptArt LawryView Answer on Stackoverflow
Solution 6 - JavascriptDavid CallizayaView Answer on Stackoverflow
Solution 7 - JavascriptPeter Lakatos - appnomadsView Answer on Stackoverflow
Solution 8 - JavascriptFerdinand LiuView Answer on Stackoverflow
Solution 9 - JavascriptPete BloisView Answer on Stackoverflow
Solution 10 - JavascriptLSOJView Answer on Stackoverflow
Solution 11 - JavascriptgeuisView Answer on Stackoverflow
Solution 12 - JavascriptRon NewcombView Answer on Stackoverflow
Solution 13 - JavascriptJustin LoveView Answer on Stackoverflow
Solution 14 - JavascriptTravis TidwellView Answer on Stackoverflow