createElement script ,async=false,don't work - javascript

createElement script ,async=false,don't work
<script>
let script = document.createElement('script');
script.src = 'a.js';
script.async = false;
document.head.appendChild(script);
console.log(a); // undefined
</script>
a.js is just var a = 123;createElement script is async default,so i use async=false,but it look like don't work

Yes, <script> elements that are not "parser inserted" have their force async flag set to true. Setting the async attribute will set that flag to false, but then, it will be either async per the attribute, or it will only make it appended in the list of scripts that will execute in order as soon as possible instead of in the set of scripts that will execute as soon as possible. This will ensure that if you do load another script that way, they'll load in the order you defined, but they will still be loaded in parallel.
You can however add a load event handler on your <script> to wait until it's properly loaded.
const el = document.createElement("script");
el.src = "https://app.requestly.io/delay/2000/https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js";
el.async = false;
el.onload = (evt) => console.log(typeof jQuery);
document.body.append(el);

Related

Does this Funnelytics code contain a race condition?

I'm looking into tracking scripts that I've come across. Here's one by Funnelytics. At first look it seems like it has a bug:
(function(funnel) {
var insert = document.getElementsByTagName('script')[0],
script = document.createElement('script');
script.addEventListener('load', function() {
window.funnelytics.init(funnel, false);
});
script.src = 'https://cdn.funnelytics.io/track.js';
script.type = 'text/javascript';
script.async = true;
insert.parentNode.insertBefore(script, insert);
})('8889dbc2-6c2f-5ba4-c201-dc8889dbc26c');
Isn't it possible that the function triggered by load will be called before the asynchronous script track.js gets executed? In which case, won't the line window.funnelytics.init fail, since window.funnelytics hasn't been defined yet?
This code does not contain a race condition. Notice that the event listener is attached to the script element, not the window object:
script.AddEventListener('load', function() { // ...
This function will only get called once the script loads and is executed.
Even if the event listener had been attached to the window object, this code would still not contain a race condition. The function would only get called once all the window's subresources get loaded and executed, including any async scripts that are dynamically inserted, as happens here.

Overwriting of the document.write to delay a script's execution

I'm try for delaying the execution of this ad script:
<script type="text/javascript">
var _pop = _pop || [];
_pop.push(['siteId', 809347]);
_pop.push(['minBid', 0.000000]);
_pop.push(['popundersPerIP', 0]);
_pop.push(['delayBetween', 0]);
_pop.push(['default', false]);
_pop.push(['defaultPerDay', 0]);
_pop.push(['topmostLayer', false]);
(function() {
var pa = document.createElement('script'); pa.type = 'text/javascript'; pa.async = true;
var s = document.getElementsByTagName('script')[0];
pa.src = '//URL/pop.js';
pa.onerror = function() {
var sa = document.createElement('script'); sa.type = 'text/javascript'; sa.async = true;
sa.src = '//URL/pop.js';
s.parentNode.insertBefore(sa, s);
};
s.parentNode.insertBefore(pa, s);
})();
</script>
For do this I have apply setTimeout in this way:
setTimeout (function() {
(function() {
var pa = document.createElement('script'); pa.type = 'text/javascript'; pa.async = true;
var s = document.getElementsByTagName('script')[0];
pa.src = '//c1.popads.net/pop.js';
pa.onerror = function() {
var sa = document.createElement('script'); sa.type = 'text/javascript'; sa.async = true;
sa.src = '//c2.popads.net/pop.js';
document.head.appendChild(sa, s);
};
document.head.appendChild(pa, s);
})(); }, 2300);
</script>
And changed s.parentNode.insertBefore with document.head.appendChild
The script start but I not see delay.
I have read "If the target script expects to be run synchronously and uses document.write you're out of luck. Unless, you want to do some messy hacks involving overwriting of the native document.write function.
I need for overwriting document.write?
You can delay the execution of any script by many methods. Simple ones:
Place your script tags just before the end of your page body, so that they are loaded after the browser has parsed the rest of the page. Therefore the browser is able to render the page before your script executes.
Add the defer="defer" attribute to your script tag (if it loads an external file). Execution is delayed at the end of page parsing.
Use a setTimeout wrapped around the code to be delayed, as you tried. Make sure the setTimeout itself is wrapped to be executed on DOM ready event, otherwise it will start the counter at execution, before the rest of the page is rendered, and you may see no effect if that rendering is slow.
Load a script asynchronously after a given timeout (but refer to point 3 about DOM ready event). In your case, why not putting the ad loader code in an external file (so you do not even have to modify it!), and load it later (using a similar script loading method)? Yes, that would be loading a loader…
Side notes:
Use of someScriptElement.parentNode.insertBefore is considered a better practice than document.head.appendChild, as a document may not have a head tag.
The sentence you quoted means that any script that relies on document.write forces you to load it synchronously (and therefore document.write should be avoided whenever possible). You may try to emulate the synchronous load by overwriting the document.write function, so that you could nevertheless load the script asynchronously. But in your case, the ad code does not use document.write, and it loads another script asynchronously, so you do not have to bother.

How to add new script and wait for it to execute in Javascript

Let's say I've got my-file.js or some CDN file in different server that has
for (var i = 0; i < 1000; i ++) {
//something really long and hard to execute
}
//after this long execution
window.myObj = {
//initialization of some global object that I need
}
(I cannot change my-file.js...)
I want to add my-file.js asynchronously to page, and then, after it is loaded and EXECUTED I want to call some event like:
//when my my-file.js is loaded I finally use window.myObj
window.myObj.somefunc(); //yaaay
Is it possible? (cross browser for IE9+ is important for me, but any not critical)
Note:
In case file I need is on CDN or somewhere on different server - I need to remember about cross-domain policy, so I think loading content of file in ajax is not possible in such case.
Note 2:
http://www.chromestatus.com/features/5711871906676736 there is exacly what I need, but I bet it'll be few years before you can easly use it globally.
var script = document.createElement('script');
script.onload = function(){
// loaded
}
document.head.appendChild(script);
script.src = "path/to/script";
That's about the simplest example. And yes, the entire thing is async, hence it needed the onload handler.
You could also wrap it up in a function:
function getScript(src,callback){
var script = document.createElement('script');
script.onload = callback;
document.head.appendChild(script);
script.src = src;
}
getScript('path/to/js',function(){
//loaded
});
However, thinking out of the box, it sounds like you need dependency management. Use RequireJS. Should fit your needs.
jQuery getScript has a callback you can use to execute events after a file is loaded, or you can use script.onload as in Joseph's example:
http://api.jquery.com/jquery.getscript/
If you want to wait until a certain property is available (ie after some work has finished), you can create an interval and then clear it as soon as the property is found in window, ie:
var waitForObj = setInterval(function(){
if(window.myObj && window.myObj.somefunc){
clearInterval(waitForObj);
//Do some stuff you need this object for
}
}, 100);

Javascript: Create Script does not work

I'm wiriting on a Chrome Extension. I need too check if a URL is online or not. The URL returns me a variable, so if the var is true, the URL is online.
In case the URL is offline it takes about 2 seconds for the error, so the Extension popup needs 2 seconds to start EVERYtime.
This is my "old" version:
popup.html:
<script language="javascript" src="http://example.org/jdcheck.js"></script>
<script language="javascript" src="popup.js"></script>
popup.js:
if (variable) { [...] }
Well, that worked - after 2 seconds.
Now I had an idea, so I removed the scriptlink in the popup.html.
And that's my new popup.js:
background.$(document).ready(function() {
var jq = document.createElement('script'); jq.type = 'text/javascript';
jq.src = 'http://127.0.0.1:9666/jdcheck.js';
document.getElementsByTagName('head')[0].appendChild(jq);
if(jdownloader){
[...action]
}
});
You see, I use jQuery to load the Checkfile.
Now, it throws me an error:
Uncaught ReferenceError: jdownloader is not defined
Well, it looks like the createElement did not work.
I'm 100% sure that the URL gives me the variable I want.
Could you please help me? I don't know how to solve this..
Thank you!
Markus
edit: I removed the jQuery part, added the keepGoing and the jq.onload:
function keepGoing() {
console.log("JS should have been loaded");
if(jdownloader){
[action]
}
}
var jq = document.createElement('script');
jq.onload = keepGoing();
jq.src = 'http://127.0.0.1:9666/jdcheck.js';
document.getElementsByTagName('head')[0].appendChild(jq);
NOW, the console gives me this:
JS should have been loaded popup.js:98
Uncaught ReferenceError: jdownloader is not defined popup.js:100
So it seems like the jdownloader var is not passed to the popup.js.
Why..why?! I don't know.
Markus
When you append a script tag to the DOM, the code does not wait for the browser to download and evaluate the script before continuing.
So you have to check back. In Chrome, you can use the load event on the script element.
background.$(document).ready(function() {
var jq = document.createElement('script'); jq.type = 'text/javascript';
jq.onload = keepGoing; // Or an anonymous inline function if you prefer
jq.src = 'http://127.0.0.1:9666/jdcheck.js';
document.getElementsByTagName('head')[0].appendChild(jq);
function keepGoing() {
if(jdownloader)...
}
});
("On Chrome" because in older versions of IE, script didn't fire the load event, it fired readystatechange.)
Side note: You don't have to supply the type attribute if it's text/javascript. That is, and always has been, the default.
The solution may be quite simple, at least for the edited part of your question.
See my jsfiddle for reference: http://jsfiddle.net/6kkC4/1/
jq.onload = keepGoing();
vs.
jq.onload = keepGoing;
Calling "onLoad()" will evaluate the function immediately (so not onload).
With jq.onload = keepGoing; only a reference to the function is passed
and the function is evaluated onload.
Since the "creating a script node" method for doing a JavaScript file Include is asynchronous, we should be using the await and async keywords.
Here is a complete example of how to Include a function called "Context" and then to call it, using Promises (await and async):
CallContext(); // Call the async function to do the call
async function CallContext()
{
await Include("context.js");
alert(Context()); // Call Context when Include is done
} // CallContext
// Wrap the callback of the script element creation in a Promise
async function Include(jsFilePath)
{
return new Promise((resolve,reject) =>
{
var js = d.createElement("script");
js.onload = resolve;
js.src = jsFilePath;
d.body.appendChild(js);
});
} // Include
Note that, as yet, there is no modern way to hide the explicit "new Promise" code.

Trying to fire the onload event on script tag

I'm trying to load a set of scripts in order, but the onload event isn't firing for me.
var scripts = [
'//cdnjs.cloudflare.com/ajax/libs/less.js/1.3.3/less.min.js',
'//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0-rc.3/handlebars.min.js',
MK.host+'/templates/templates.js'
];
function loadScripts(scripts){
var script = scripts.shift();
var el = document.createElement('script');
el.src = script;
el.onload = function(script){
console.log(script + ' loaded!');
if (scripts.length) {
loadScripts(scripts);
}
else {
console.log('run app');
MK.init();
}
};
$body.append(el);
}
loadScripts(scripts);
I guess native events like el.onload don't fire when jQuery is used to append the element to the DOM. If I use native document.body.appendChild(el) then it fires as expected.
You should set the src attribute after the onload event, f.ex:
el.onload = function() { //...
el.src = script;
You should also append the script to the DOM before attaching the onload event:
$body.append(el);
el.onload = function() { //...
el.src = script;
Remember that you need to check readystate for IE support. If you are using jQuery, you can also try the getScript() method: http://api.jquery.com/jQuery.getScript/
I faced a similar problem, trying to test if jQuery is already present on a page, and if not force it's load, and then execute a function. I tried with #David Hellsing workaround, but with no chance for my needs. In fact, the onload instruction was immediately evaluated, and then the $ usage inside this function was not yet possible (yes, the huggly "$ is not a function." ^^).
So, I referred to this article : https://developer.mozilla.org/fr/docs/Web/Events/load
and attached a event listener to my script object.
var script = document.createElement('script');
script.type = "text/javascript";
script.addEventListener("load", function(event) {
console.log("script loaded :)");
onjqloaded(); // in fact, yourstuffscript() function
});
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
For my needs, it works fine now. Hope this can help others :)

Categories

Resources