Is it the last `script` element the currently running script?
Asked Answered
C

3

8

Is it safe to assume that the last script element* in the document when the script runs** is the currently running script?

For example, I want to create a script that can be dropped anywhere in the body of of a page and display an element in the same place. I'm doing something like this:

function getCurrentScriptElement() {
    var scripts = document.getElementsByTagName('script');
    return scripts[scripts.length - 1];
}

var script = getCurrentScriptElement();
var view = document.createElement('span');

/* Put stuff in our view... */

script.parentNode.insertBefore(view, script);

Assuming the script is in the body of the document, is this "safe?" Will the getCurrentScriptElement function always return the running script? If not, how can it be done?

I'd like to do this without tying the script to a specific id attribute or similar, I'd like it to just be positional.


I created an example here that pulls in this script. One answer suggested that other scripts could create a condition where an example like this would break. Is it possible to add other scripts to this example that will break it?


It was suggested that other scripts with defer or async attributes could break this. Can anyone give an example of how such a script might work?

As I understand it, defer means load the DOM first, and then run the script with the defer tag. How would the defer attribute appearing on another script element affect the behavior of getCurrentScriptElement?

async, as I understand it, means start fetching that script and keep parsing the DOM at the same time, don't wait... but when it hits my script it should still stop and wait, right?

I don't see how either one could affect it, can anyone provide an example?


* I'm only interested in external scripts for the purpose of this question.

** Not the last script element in the entire document, but the last script element in the document at the time when it runs. The rest of the document shouldn't be loaded yet, right?

Commodity answered 19/1, 2013 at 0:55 Comment(1)
Well, to begin with, your example does not seem to work in IE9.Lamberto
L
5

It's not an absolute guarantee no. Check out this JSFiddle: http://jsfiddle.net/jAsek/

<!DOCTYPE html>
<title>Test case</title>
<div>
    <p>At the start</p>
    <script id="first">
        var scr1 = document.createElement("script");
        scr1.setAttribute("id", "early");
        document.body.appendChild(scr1);
    </script>
    <p>After the first script</p>
    <script id="second">
        function getCurrentScriptElement() {
            var scripts = document.getElementsByTagName('script');
            return scripts[scripts.length - 1];
        }

        alert(getCurrentScriptElement().id);
    </script>
    <p>At the end</p>
</div>

Here the alert reports the id of the injected script "early", not the id of currently running script "second".

There's no practical difference between internal and external scripts.

Landri answered 19/1, 2013 at 1:54 Comment(12)
Nice, didn't think of that. I wonder if it can be worked around. Could definitely happen if something else fired off a JSONP request. The external scripts thing was sort of in response to a comment on another answer.Commodity
Interesting. It's definitely not as simple as I thought.Amphitrite
Thinking about making it a rule that the script can't go directly in the body, has to be nested in something else (this fits the actual use case I have in mind). I think then we can work around this...Commodity
No, the first script could easily create a div as well, put the "early" script in the div and append the div to the body.Landri
@GGG How about checking the text content of the script node for a unique string present on your code? If it's not there, check the previous script block, and so on.Amphitrite
Or, if you're okay with rules, why not simply requiring a fixed, unique id on the script tag?Amphitrite
yeah, I guess I'll have to do something like that, was just hoping for a cleaner solution... I don't think it's likely that other scripts will be injecting scripts wrapped in divs, but i guess it could happen.Commodity
Ah, wait, I already made a rule... it's only for external scripts, so the src attribute will be there. I can just check that. Still ugly, though.Commodity
Made another question about the example above, I think this answers this question :) #14411248Commodity
@GGG - Looks a nice idea. Can't think of any pitfalls that you're not already aware of.Landri
I think I might have a better solution stackoverflow.com/questions/18926353Eden
document.currentScript is not affected by the bug. stackoverflow.com/questions/18926353 But it doesn't work for IEPlenipotentiary
L
1

I don’t think it’s a safe assumption at all, as browsers execute javascript code quite differently depending on a number of things (like if you have other script elements in the head, if they are external etc.).

You should just require people to use a dummy element with a custom id or class. That way you will also make it possible to do whatever you do multiple times a page without having to run the script multiple times.

This is also what is done when using widgets, for example Google’s +1 button.

An alternative would be to use document.write to write additional content while the script is executed. This will not replace the script tag however, but simply add something after it.

Lamberto answered 19/1, 2013 at 1:3 Comment(6)
Can you be a little more clear about what you mean by the first paragraph, or provide an example? If I provide an example of a script that works, can you break it by adding another script?Commodity
Unfortunately, I can’t really give you an example or further details (I actually wanted to write that as a comment, but didn’t because of the second part of my answer). It’s just from experience and previous discussions, browser behave very differently on when and how JS is loaded and executed.Lamberto
I'm not interested in the second part (no offense)... I guess I should add that to the question.Commodity
@GGG Examples include <script defer> and <script async>, which both affect the timing of execution and may render getCurrentScriptElement() inaccurate. document.currentScript seems to be intended/proposed solution, but doesn't seem to be well-supported yet.Tremulant
Jonathan Lonowski, hmm, maybe the kind of answer I'm looking for could include checks for those?Commodity
To whoever is upvoting that comment above, please see my updates... an example would be really convincing.Commodity
P
1

You probably want to use document.currentScript that is currently supported by 90% of browsers and fallback to document.scripts[document.scripts.length-1] if you're targetting IE

function writeHere(element)
{
 var sc = document.currentScript || document.scripts[document.scripts.length-1] ;  
 sc.parentNode.insertBefore(element, sc);
 // or in jquery $(sc).before($(element));
}

note: I didn't test document.scripts[document.scripts.length-1] thoroughly but it should work in most cases (but not in Alohci exemple).
And this is a fix for IE so who cares :)

Plenipotentiary answered 18/10, 2018 at 8:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.