Dynamic javascript loading and execution - javascript

I'm trying to dynamically load a Javascript based on the return value of an API call. I dynamically insert the script tag but it does not get executed. Can someone help understand why? The relevant code snippet is pasted below
onError: function(code) {
if(code == "false") {
var headID = document.getElementsByTagName("head")[0];
var scriptTag = document.createElement("script");
scriptTag.type="text/javascript";
scriptTag.src= 'scriptURL';
headID.appendChild(scriptTag);
}
}
Using firebug/chrome inspector, I can see that the script tag is added to the dom but the script is not executed (at least not that I can determine). It is a 3rd Party script hence I do not have direct control over it and hence cannot modify it either.

After reading the comments below the question it seems that the third party script is doing its job on window.onload event. Many programmers use this style.
window.onload = function() {
// Whatever task
};
If the onload event of your page has already been fired before you add the script tag dynamically, the 'Whatever task' code would never execute.
Check the source of the third party script. If it uses window.onload, you can try calling window.onload(); after you add the script tag dynamically.

Related

Is there any advantage to add 'defer' to a new script tag after $(document).ready()?

I have some javascript that is not required for my initial page load. I need to load it based on some condition that will be evaluated client-side.
$(document).ready(function() {
let someCondition = true; // someCondition is dynamic
if (someCondition) {
var element = document.createElement('script');
element.src = 'https://raw.githubusercontent.com/Useless-Garbage-Institute/useless-garbage/master/index.js';
element.defer = true; // does this make a difference?
element.onload = function() {
// do some library dependent stuff here
document.getElementById("loading").textContent = "Loaded";
};
document.body.appendChild(element);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 id="loading">Loading...</h1>
Does it make a difference (in terms of how browser will treat the script tag), if a new tag created using javascript, after document is ready, has 'defer' attribute or not? I think there is no difference, but how can I say for sure?
I believe I understand how deferred scripts behave when script tag is part of the initial html (as described here). Also, this question is not about whether element.defer=true can be used or not (subject of this question).
No that doesn't make any difference, the defer attribute is ignored in case of "non-parser-inserted" scripts:
<script defer src="data:text/javascript,console.log('inline defer')"></script>
<script>
const script = document.createElement("script");
script.src = "data:text/javascript,console.log('dynamic defer')";
script.defer = true;
document.body.append(script);
</script>
<!-- force delaying of parsing -->
<script src="https://deelay.me/5000/https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Look at your browser's console or pay attention to the logs timestamps to see that the dynamically inserted script actually did execute while we were waiting for the delayed script to be fetched.
There's a difference between adding them to the function and adding directly the CDN ( especially in your case ).
Let's look at the code execution of the above-mentioned code first,
You have added the jquery CDN first ( without defer ) so that loads first.
$(document).ready will be fired once after the complete load of jquery.
There'll be the creation and insertion of a new script tag to the dom.
Download the https://raw.githubusercontent.com/Useless-Garbage-Institute/useless-garbage/master/index.js asynchronously.
Let's look at another approach: adding CDN to the code:
Your DOM will have 2 script tags.
Both will start loading based on the type of load parallelly ( defer async etc ).
Notice you are not waiting for the dom ready event to load the second script.
I suggest adding only the main JS part in a js file and adding it to the CDN. Others can wait load with the delay.
In case you are really needed with a js src, then don't load it the first way since it waits for the complete page load.
I suggest you read and look at web-vitals and SEO for this.
and for your other question, yes you can add defer attribute with element.defer=true to the elements while creating and loading to DOM.
Hope this answer helps you!
Feel free to comment if you get any errors or doubts.
I think the JQuery Arrive lib will solve your case.

How to remove inline styles added by external script?

I have a <script> that generates a both <style> and inline style attributes with !important tags. I'd like to remove all this styling.
My plan was to use a javascript onload callback (and some jQuery) to remove the <style> block and all inline style attributes — but I can't seem to select any of these elements. Here's what I've been toying with:
var script = document.createElement("script");
script.src = "//script.path.js";
script.onload = function(){
$(this).parent().find("style").remove();
$(this).parent().find("[style]").removeAttr("style");
};
$(target).append(script);
UPDATE
It seems that the elements generated by the <script> just aren't available in the DOM right away. If I use setInterval to check if the elements exist first, I can get this to work. I imagine there's a better way to do this though...
According to this other question, you must append the script tag to the DOM before setting onload.
var script = document.createElement("script");
$(target).append(script);
script.src = "//script.path.js";
script.onload = function(){
$(this).parent().find("style").remove();
$(this).parent().find("[style]").removeAttr("style");
};
https://jsbin.com/minoyeyicu/edit?html,js,output
UPDATE: Having clarified that the issue is that the style tag/attributes haven't yet been applied to the DOM until after the downloaded script has executed, one alternative (depending on whether the loaded script is under your control), is to pass a callback parameter to the loaded script and have the loaded script execute the callback when it finishes executing (which is how the Google Maps API works). E.g.
script.src = '//script.path.js?callback=removeStyles'
In order to use the callback parameter from within script.path.js, something like this could be done.

How to remove HTML script tag with open type [duplicate]

How can I remove script elements before they are being executed?
I thought about using the DOMNodeInserted event, but apparently it doesn't catch script elements. I've also tried using the jQuery livequery plugin like that:
$("script").livequery(function () {
$(this).remove();
});
It did remove the script element, but after it was executed.
I'm looking for a cross-browser solution, but I'm not even sure if that's possible. I read about Mutation Observers which seems close enough but I'm not sure if it can solve my problem.
It would be even better if there was a way to modify the script content before it is being executed without removing and recreating it.
Removing a script element does not do anything. If you can somehow access a script element, it was executed a long time ago and removing it will have no effect.
So we need to work around it. If your script element is at the top of the page like this:
<head>
<script src="yourscript.js"></script>
You could make a synchronous ajax request to the same page, so you can parse its content into a new document, modify all script tags and then replace
the current document with the modified document.
var xhr = new XMLHttpRequest,
content,
doc,
scripts;
xhr.open( "GET", document.URL, false );
xhr.send(null);
content = xhr.responseText;
doc = document.implementation.createHTMLDocument(""+(document.title || ""));
doc.open();
doc.write(content);
doc.close();
scripts = doc.getElementsByTagName("script");
//Modify scripts as you please
[].forEach.call( scripts, function( script ) {
script.removeAttribute("src");
script.innerHTML = 'alert("hello world");';
});
//Doing this will activate all the modified scripts and the "old page" will be gone as the document is replaced
document.replaceChild( document.importNode(doc.documentElement, true), document.documentElement);
Unfortunately this cannot be set up in jsfiddle or jsbin. But you should be able to copy paste this code exactly as it is into this
page's console in google chrome. You should see the alerts and when you inspect the live dom, each script was modified.
The difference is that we are running this after scripts have been executed on the page, so the old scripts should still have a working effect on the page.
That's why, for this to work, you need to be the very first script on the page to do it.
Tested to work in google chrome. Firefox is completely ignoring the doc.write call for some reason.
i donot know what you are trying to do. But it is better to load them on request rather than delete on some conditions.
$.getScript('helloworld.js', function() {
$("#content").html('
Javascript is loaded successful!
');
});
If you wants to remove scripts before there execution, its not possible.
But what you can do is, remove script programatically on a condition & if have an issue with memory-leaks, then you can call below code before remove script.
var var1 = 'hello';
var cleanAll = function () {
delete window.var1;
delete window.cleanAll;
};
// unload all resources
cleanAll();

Asynchronous script load issue

I'm trying to load a script from an external source after the user executes a click function. Like so:
$("#test-button").click(function() {
$.getScript("http://someurl.com/widget/javascript?key=4&t=uid&q=94777&show=all", function (data) {
$('#myDiv').append(data);
});
});
The script I'm loading (which I have no control over) includes a document.write, so I'm getting this error when I execute the click:
It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.
I'm not sure how to get around this. I thought getScript would manage this.
It seems like you have append the script like text
please see this example
var script = document.createElement('script');
script.src = '../web3.0/lib/charts/google-charts.js';
document.body.appendChild(script);

Count number of async scripts in the DOM

I have a script script.js that calls in advertisements into the DOM. It is invserted after #closeImage if some test is true:
<div id="overlay">
<img id="closeImage" src="close100x100.png">
</div>
If the test is true I call my script.
if (test) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'script.js';
$("#closeImage").append(script);
script.js might not find an advertisement. In this case from within script.js, script01.js which will be inserted into the DOM to look for different advertisements. If nothing is found script03.js will be called from within script02.js and so my div might end looking like this:
<div id="overlay">
<img id="closeImage" src="close100x100.png">
<script src="script.js"></script>
<script src="script01.js"></script>
<script src="script02.js"></script>
<!-- actual banner html -->
</div>
Only the first script is being inserted by the original document.createElement(). How do I count the number of scripts in #overlay? - This did not work:
$(document).ready(function () {
var loaded = $('div#ADF_overlay script').length;
});
EDIT (Restated the problem differently based on feedback)
My main guess here is that you have a timing issue. You are inserting a script, which has to load and then execute, which may then insert another script which has to load and then execute and you don't actually know when that process is done so you can check how many scripts were loaded in total.
$(document).ready() will not wait for that process to be done - it only waits for the original HTML of the document to be parsed (and any inline scripts that were there in the original HTML and don't have the defer or async attributes will run).
The only way to know when a cascade of dynamically loaded scripts are actually done is to have the last script somehow mark when it's done (either by calling a function, triggering an event, setting a variable or marking something in the DOM). Without the script telling you when it's done inserting new script tags, you can't know whether the next script is still loading and waiting to run which might insert some more scripts, etc...
We could probably help better with ideas for solving your overall problem (what you are actually trying to accomplish) if you described the overall problem rather than just this one piece that you're trying to use.
If you just want to count how many dynamically insert scripts there are, then it would be simplest to just maintain a javascript counter as you insert them and then you can use that counter sometime later.
var scriptsInserted = 0;
if (seenOverlay('served') === 'false') {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'script.js';
$("#closeImage").append(script);
++scriptsInserted;
}
Then, some time later, you can just refer to your variable scriptsInserted to access the count.
Alternatively, you can put a class name on your script elements and just query for that:
if (seenOverlay('served') === 'false') {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'script.js';
script.className = "adScript";
$("#closeImage").append(script);
++scriptsInserted;
}
And, some time later:
$(".adScript").length;
If you're dynamically inserting a cascade of scripts (insert one, which loads, runs and then inserts another, and so on), then when do you know to check to see how many scripts were actually inserted? The timing of when to check may also be an issue for you because if you're checking that in $(document).ready(), then that process may not be done yet as each dynamically inserted script is loaded asynchronously and $(document).ready() can easily fire before that process is done because it doesn't wait for dynamically inserted scripts to load or run.
It appears that you may potentially have other issues because inserting a script into a particular place in an already loaded document will usually not insert content at that place in the document because a dynamically loaded script element can't use document.write() to insert content into the existing document in the same way that a normal inline <script> tag can.

Categories

Resources