Order of script execution when using document.write - javascript

I need to run a library called Fastclick in order to handle the 300ms click delay that's on mobiles. At the same time, I don't need to run it if I'm not running on mobiles. So I'm looking to do something like this:
<head>
<script>
var UserAgent = navigator.userAgent;
if (screen.width < 851 || UserAgent.indexOf('iPad') !== -1 || UserAgent.indexOf('iPhone') || UserAgent.indexOf('Android')) {
document.write('<script src=\"/ExternalFiles/Fastclick.js\"></script>');
}
</script>
//second script
<script>
if (FastClick) {...}
</script>
</head>
As you can see, the second script checks to see if FastClick is loaded. On my local machine, this works. However, I'm wondering if it works just because the file is loaded almost instantaneously from the file system (ie no delay) or if in fact, the document.write statement triggers the load and the script execution is on hold until that script is loaded. I'm looking for the latter behavior: do scripts that are loaded via document.write pause the JavaScript parsing until they're loaded?

Why don't you include the file always and call the function only when you need it? Then you don't have to think about any possibility your way involves:
<head>
<script src="/ExternalFiles/Fastclick.js"></script>
<script>
var UserAgent = navigator.userAgent;
if (screen.width < 851 || UserAgent.indexOf('iPad') !== -1 || UserAgent.indexOf('iPhone') || UserAgent.indexOf('Android')) {
FastClick(...)
}
</script>
</head>
When a non-mobile browser has loaded the file one time, it's in the cache and doesn't slow down page load.

Related

How to trigger a javascript event when the page is loaded [duplicate]

I want to run a function when the page is loaded, but I don’t want to use it in the <body> tag.
I have a script that runs if I initialise it in the <body>, like this:
function codeAddress() {
// code
}
<body onLoad="codeAddress()">
But I want to run it without the <body onload="codeAddress()"> and I have tried a lot of things, e.g. this:
window.onload = codeAddress;
But it is not working.
So how do I run it when the page is loaded?
window.onload = codeAddress; should work - here's a demo, and the full code:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function codeAddress() {
alert('ok');
}
window.onload = codeAddress;
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function codeAddress() {
alert('ok');
}
</script>
</head>
<body onload="codeAddress();">
</body>
</html>
Rather than using jQuery or window.onload, native JavaScript has adopted some great functions since the release of jQuery. All modern browsers now have their own DOM ready function without the use of a jQuery library.
I'd recommend this if you use native Javascript.
document.addEventListener('DOMContentLoaded', function() {
alert("Ready!");
}, false);
Alternate solution. I prefer this for the brevity and code simplicity.
(function () {
alert("I am here");
})();
This is an anonymous function, where the name is not specified.
What happens here is that, the function is defined and executed together.
Add this to the beginning or end of the body, depending on if it is to be executed before loading the page or soon after all the HTML elements are loaded.
Taking Darin's answer but jQuery style. (I know the user asked for javascript).
running fiddle
$(document).ready ( function(){
alert('ok');
});​
window.onload = function() { ... etc. is not a great answer.
This will likely work, but it will also break any other functions already hooking to that event. Or, if another function hooks into that event after yours, it will break yours.
So, you can spend lots of hours later trying to figure out why something that was working isn't anymore.
A more robust answer here:
if(window.attachEvent) {
window.attachEvent('onload', yourFunctionName);
} else {
if(window.onload) {
var curronload = window.onload;
var newonload = function(evt) {
curronload(evt);
yourFunctionName(evt);
};
window.onload = newonload;
} else {
window.onload = yourFunctionName;
}
}
Some code I have been using, I forget where I found it to give the author credit.
function my_function() {
// whatever code I want to run after page load
}
if (window.attachEvent) {window.attachEvent('onload', my_function);}
else if (window.addEventListener) {window.addEventListener('load', my_function, false);}
else {document.addEventListener('load', my_function, false);}
Hope this helps :)
Try readystatechange
document.addEventListener('readystatechange', () => {
if (document.readyState == 'complete') codeAddress();
});
where states are:
loading - the document is loading (no fired in snippet)
interactive - the document is parsed, fired before DOMContentLoaded
complete - the document and resources are loaded, fired before window.onload
<script>
document.addEventListener("DOMContentLoaded", () => {
mydiv.innerHTML += `DOMContentLoaded (timestamp: ${Date.now()})</br>`;
});
window.onload = () => {
mydiv.innerHTML += `window.onload (timestamp: ${Date.now()}) </br>` ;
} ;
document.addEventListener('readystatechange', () => {
mydiv.innerHTML += `ReadyState: ${document.readyState} (timestamp: ${Date.now()})</br>`;
if (document.readyState == 'complete') codeAddress();
});
function codeAddress() {
mydiv.style.color = 'red';
}
</script>
<div id='mydiv'></div>
Take a look at the domReady script that allows setting up of multiple functions to execute when the DOM has loaded. It's basically what the Dom ready does in many popular JavaScript libraries, but is lightweight and can be taken and added at the start of your external script file.
Example usage
// add reference to domReady script or place
// contents of script before here
function codeAddress() {
}
domReady(codeAddress);
window.onload will work like this:
function codeAddress() {
document.getElementById("test").innerHTML=Date();
}
window.onload = codeAddress;
<!DOCTYPE html>
<html>
<head>
<title>learning java script</title>
<script src="custom.js"></script>
</head>
<body>
<p id="test"></p>
<li>abcd</li>
</body>
</html>
As soon as the page load the function will be ran:
(*your function goes here*)();
Alternatively:
document.onload = functionName();
window.onload = functionName();
I believe this is the best way to maintain support across different versions of browsers
if (window.addEventListener) {
window.addEventListener("load", myFunction, false);
}
else if (window.attachEvent) {
window.attachEvent("onload", myFunction);
}
else {
window.onload = myFunction; //will override previously attached event listeners.
}
Universal Cross-Browser Web Page Loader
I wrote a JavaScript page loader that should solve your issues loading a function after the page is loaded. This web page loader is 99.9% cross-browser compatible and works in many versions of browsers, old and new, unlike the other posts. Includes support for loading pages in nearly all browsers, including Internet Explorer 3-11, all Firefox and Chrome browsers, early Opera, all mobile browsers, Netscape 4 and 6 series, etc.
It will pick the fastest page load event or state check for a given browser and return a text string indicating JavaScript may safely process the Document Object Model (DOM). Should work in many legacy browsers, but test. Place your JavaScript functions or or library calls inside the "Start()" method below, so they are triggered as soon as the script says the web page or DOM is loaded in the browser.
As a side note, I recommend you place this code either:
In the head of your html page in a embedded <script> block as a synchronous script, which pauses the page to load early.
...or...
In a loaded external <script> tag file with the "async" attribute added so it loads quietly in parallel to your page but pauses html loading when download complete so it gets parsed and processed first.
The script should not render-block much if using these methods. You want this script ready when the web page DOM is first built and not after, especially since later states of the page could get delayed waiting for images, videos, and JavaScript API's to download.
// ======== AFTER PAGE LOADS CALL YOUR SCRIPTS HERE =========
function Start(status) {
// In most modern browsers the console should return:
// "Browser Loader : Document : DOMContentLoaded : interactive"
console.log(status);
// add your script calls here...
};
// ======== JAVASCRIPT PAGE LOADER =========
// Stokely Web Page loader, 2022
if (document.readyState) {
if (document.readyState === "complete" || document.readyState === "loaded") {
Start("Browser Loader : Document : readyState : complete");
} else {
if (window.addEventListener) {
// Never try and call 'DOMContentLoaded' unless the web page is still in an early loading state.
if (document.readyState === 'loading' || document.readyState === 'uninitialized') {
window.addEventListener('DOMContentLoaded', function () {
// Most modern browsers will have the DOM ready after this state.
if (document.readyState === "interactive") {
Start("Browser Loader : Document : DOMContentLoaded : interactive");
} else if (document.readyState === "complete" || document.readyState === "loaded") {
Start("Browser Loader : Document : DOMContentLoaded : complete");
} else {
Start("Browser Loader : Document : DOMContentLoaded : load");
}
}, false);
} else {
// FALLBACK LOADER : If the readyState is late or unknown, go ahead and try and wait for a full page load event. Note: This function below was required for Internet Explorer 9-10 to work because of non-support of some readyState values! IE 4-9 only supports a "readyState" of "complete".
if (document.readyState === 'complete' || document.readyState === "loaded") {
Start("Browser Loader : Document : readyState : complete");
} else {
window.addEventListener('load', function () {
Start('Browser Loader : Window : Event : load');
}, false);
}
}
// If 'addEventListener' is not be supported in the browser, try the 'onreadystate' event. Some browsers like IE have poor support for 'addEventListener'.
} else {
// Note: document.onreadystatechange may have limited support in some browsers.
if (document.onreadystatechange) {
document.onreadystatechange = function () {
if (document.readyState === "complete" || document.readyState === "loaded"){
Start("Browser Loader : Document : onreadystatechange : complete");
}
// OPTIONAL: Because several versions of Internet Explorer do not support "interactive" or get flagged poorly, avoid this call when possible.
//else if (document.readyState === "interactive") {
//Start("Browser Loader : Document : onreadystatechange : interactive");
//}
}
} else {
// Note: Some browsers like IE 3-8 may need this more traditional version of the loading script if they fail to support "addeventlistener" or "onreadystate". "window.load" is a very old and very reliable page loader you should always fall back on.
window.onload = function() {
Start("Browser Loader : Window : window.onload (2)");
};
}
}
}
} else {
// LEGACY FALLBACK LOADER. If 'document.readyState' is not supported, use 'window.load'. It has wide support in very old browsers as well as all modern ones. Browsers Firefox 1-3.5, early Mozilla, Opera < 10.1, old Safari, and some IE browsers do not fully support 'readyState' or its values. "window.load" is a very old and very reliable page loader you should always fall back on.
window.onload = function () {
Start("Browser Loader : Window : window.onload (1)");
};
};
Note: When you run this script in a web browser, be sure to press F12 to pull up the browser tools screen and check the console tab to see the result. It will tell you at what stage the web page loader was triggered and when it called the 'Start()' script.
In most modern browsers (HTML5 or post-2010) it should be triggered as soon as the DOM or Document Object Model of HTML markup is rendered but the rest of the web page resources, CSS, images, video, and other files are still loading. In modern browsers this is usually between a readystate of "interactive" and "complete" and the DOM is built but the browser is still downloading other resource files. This allows your JavaScript to access and start manipulating the HTML tree or DOM very very early.
Older browsers like Internet Explorer v. 3-8 or Netscape, do not understand the advanced DOM checks so would require the full or "complete" load of the DOM and all page resources before calling your JavaScript.

jQuery CDN timeout fallback

I have read in several places how to fallback on a local copy of the jQuery library should the link hosted by either google or microsoft or other fail.
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
if (typeof jQuery == 'undefined')
{
document.write(unescape("%3Cscript src='/Scripts/jquery-1.3.2.min.js' type='text/javascript'%3E%3C/script%3E"));
}
</script>
My application works within an intranet environment however and occasionally the external jQuery link doesn't so much fail but takes a long time to load (due to external internet connection issues).
I'm wondering if there is a way to not only use such a fallback but set a timeout for the CDN link so that if the link takes a certain amount of time it should fail and call on the fallback.
Something like:
if(timetoloadjquery > n) {
Use fallback local jQuery library.
}
Perhaps some kind of loop that checks if the jQuery is defined and if after so many iterations it is not....do something else?
Thanks for the help.
This may help you. After 5-seconds have passed, Javascript checks if jQuery is available, if not, then loads the library from local server.
1. With a timer
<script>
setTimeout(function() {
if(window.jQuery) return;
var n = document.getElementsByTagName("script")[0];
n.parentNode.insertBefore(document.createElement("script"), n).src = "assets/jQuery/jquery-1.7.2.min.js";
}, 5000);
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
2. This one doesn't have a timer, it loads a local version if the CDN version fails.
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.js"></script>
<script type="text/javascript">window.jQuery || document.write("<script type='text/javascript' src='js/jquery-1.8.3.min.js'>\x3C/script>")</script>

Internet Explorer not loading some .js files

So, I've got this aspx page which includes 4 javascipt files like so:
<script src="Scripts/invoicePrimary.js" type="text/javascript"></script>
<script src="Scripts/invoiceBillOfLading.js" type="text/javascript"></script>
<script src="Scripts/invoiceCharge.js" type="text/javascript"></script>
<script src="Scripts/invoiceInvoice.js" type="text/javascript"></script>
Each of those defines a variable. invoicePrimary declares classPrimary, invoiceBillOfLading defines classBillOfLading, etc.
Later on in the page, we make reference to classPrimary, which makes reference to the others. The problem is that only half of them are defined. Specifically, classPrimary and classCharge are defined, while classBillOfLading an classInvoice are undefined.
This only happens in IE. Both in IE9 and IE9 acting as IE8. The whole site is designed for IE, so the rest of the stuff doesn't work in other browsers, but this part does. Does anyone have any ideas? Edit: As a note, all files are loaded as seen in Visual Studio's Script Documents folder.
Edit: The actual code which has problems:
populateScreenFromObject: function() {
if (invoiceFolder == null) return;
if (invoiceFolder.BillOfLadings != null) classBillOfLading.AddList(invoiceFolder.BillOfLadings);
if (invoiceFolder.Invoices != null) classInvoice.AddList(invoiceFolder.Invoices);
if (invoiceFolder.Charges != null) classCharge.FirstTimeLoad(invoiceFolder.Charges);
classInvoice.FirstTimeLoad();
classCharge.DisableNonDraftRadios();
classBillOfLading.Add(); // In case BOL number is prepopulated
},
This is a function in classPrimary, which is called from the main page like so: classPrimary.PopulateScreen(); At this point, classCharge is the only one of those classes which is defined. The only thing that happens before this is loading invoiceFolder data from json stored in a hidden field.
As it turns out, the problem was that the invoiced*.js files that weren't loading had a , at the end of the last function, so it wasn't expecting to end. IE8 just ignored this, as did other browsers, but IE9 threw a fit.
Try it in the real IE8. IE9 has caused a lot of breakage in IE.

How to run a function when the page is loaded?

I want to run a function when the page is loaded, but I don’t want to use it in the <body> tag.
I have a script that runs if I initialise it in the <body>, like this:
function codeAddress() {
// code
}
<body onLoad="codeAddress()">
But I want to run it without the <body onload="codeAddress()"> and I have tried a lot of things, e.g. this:
window.onload = codeAddress;
But it is not working.
So how do I run it when the page is loaded?
window.onload = codeAddress; should work - here's a demo, and the full code:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function codeAddress() {
alert('ok');
}
window.onload = codeAddress;
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function codeAddress() {
alert('ok');
}
</script>
</head>
<body onload="codeAddress();">
</body>
</html>
Rather than using jQuery or window.onload, native JavaScript has adopted some great functions since the release of jQuery. All modern browsers now have their own DOM ready function without the use of a jQuery library.
I'd recommend this if you use native Javascript.
document.addEventListener('DOMContentLoaded', function() {
alert("Ready!");
}, false);
Alternate solution. I prefer this for the brevity and code simplicity.
(function () {
alert("I am here");
})();
This is an anonymous function, where the name is not specified.
What happens here is that, the function is defined and executed together.
Add this to the beginning or end of the body, depending on if it is to be executed before loading the page or soon after all the HTML elements are loaded.
Taking Darin's answer but jQuery style. (I know the user asked for javascript).
running fiddle
$(document).ready ( function(){
alert('ok');
});​
window.onload = function() { ... etc. is not a great answer.
This will likely work, but it will also break any other functions already hooking to that event. Or, if another function hooks into that event after yours, it will break yours.
So, you can spend lots of hours later trying to figure out why something that was working isn't anymore.
A more robust answer here:
if(window.attachEvent) {
window.attachEvent('onload', yourFunctionName);
} else {
if(window.onload) {
var curronload = window.onload;
var newonload = function(evt) {
curronload(evt);
yourFunctionName(evt);
};
window.onload = newonload;
} else {
window.onload = yourFunctionName;
}
}
Some code I have been using, I forget where I found it to give the author credit.
function my_function() {
// whatever code I want to run after page load
}
if (window.attachEvent) {window.attachEvent('onload', my_function);}
else if (window.addEventListener) {window.addEventListener('load', my_function, false);}
else {document.addEventListener('load', my_function, false);}
Hope this helps :)
Try readystatechange
document.addEventListener('readystatechange', () => {
if (document.readyState == 'complete') codeAddress();
});
where states are:
loading - the document is loading (no fired in snippet)
interactive - the document is parsed, fired before DOMContentLoaded
complete - the document and resources are loaded, fired before window.onload
<script>
document.addEventListener("DOMContentLoaded", () => {
mydiv.innerHTML += `DOMContentLoaded (timestamp: ${Date.now()})</br>`;
});
window.onload = () => {
mydiv.innerHTML += `window.onload (timestamp: ${Date.now()}) </br>` ;
} ;
document.addEventListener('readystatechange', () => {
mydiv.innerHTML += `ReadyState: ${document.readyState} (timestamp: ${Date.now()})</br>`;
if (document.readyState == 'complete') codeAddress();
});
function codeAddress() {
mydiv.style.color = 'red';
}
</script>
<div id='mydiv'></div>
Take a look at the domReady script that allows setting up of multiple functions to execute when the DOM has loaded. It's basically what the Dom ready does in many popular JavaScript libraries, but is lightweight and can be taken and added at the start of your external script file.
Example usage
// add reference to domReady script or place
// contents of script before here
function codeAddress() {
}
domReady(codeAddress);
window.onload will work like this:
function codeAddress() {
document.getElementById("test").innerHTML=Date();
}
window.onload = codeAddress;
<!DOCTYPE html>
<html>
<head>
<title>learning java script</title>
<script src="custom.js"></script>
</head>
<body>
<p id="test"></p>
<li>abcd</li>
</body>
</html>
As soon as the page load the function will be ran:
(*your function goes here*)();
Alternatively:
document.onload = functionName();
window.onload = functionName();
I believe this is the best way to maintain support across different versions of browsers
if (window.addEventListener) {
window.addEventListener("load", myFunction, false);
}
else if (window.attachEvent) {
window.attachEvent("onload", myFunction);
}
else {
window.onload = myFunction; //will override previously attached event listeners.
}
Universal Cross-Browser Web Page Loader
I wrote a JavaScript page loader that should solve your issues loading a function after the page is loaded. This web page loader is 99.9% cross-browser compatible and works in many versions of browsers, old and new, unlike the other posts. Includes support for loading pages in nearly all browsers, including Internet Explorer 3-11, all Firefox and Chrome browsers, early Opera, all mobile browsers, Netscape 4 and 6 series, etc.
It will pick the fastest page load event or state check for a given browser and return a text string indicating JavaScript may safely process the Document Object Model (DOM). Should work in many legacy browsers, but test. Place your JavaScript functions or or library calls inside the "Start()" method below, so they are triggered as soon as the script says the web page or DOM is loaded in the browser.
As a side note, I recommend you place this code either:
In the head of your html page in a embedded <script> block as a synchronous script, which pauses the page to load early.
...or...
In a loaded external <script> tag file with the "async" attribute added so it loads quietly in parallel to your page but pauses html loading when download complete so it gets parsed and processed first.
The script should not render-block much if using these methods. You want this script ready when the web page DOM is first built and not after, especially since later states of the page could get delayed waiting for images, videos, and JavaScript API's to download.
// ======== AFTER PAGE LOADS CALL YOUR SCRIPTS HERE =========
function Start(status) {
// In most modern browsers the console should return:
// "Browser Loader : Document : DOMContentLoaded : interactive"
console.log(status);
// add your script calls here...
};
// ======== JAVASCRIPT PAGE LOADER =========
// Stokely Web Page loader, 2022
if (document.readyState) {
if (document.readyState === "complete" || document.readyState === "loaded") {
Start("Browser Loader : Document : readyState : complete");
} else {
if (window.addEventListener) {
// Never try and call 'DOMContentLoaded' unless the web page is still in an early loading state.
if (document.readyState === 'loading' || document.readyState === 'uninitialized') {
window.addEventListener('DOMContentLoaded', function () {
// Most modern browsers will have the DOM ready after this state.
if (document.readyState === "interactive") {
Start("Browser Loader : Document : DOMContentLoaded : interactive");
} else if (document.readyState === "complete" || document.readyState === "loaded") {
Start("Browser Loader : Document : DOMContentLoaded : complete");
} else {
Start("Browser Loader : Document : DOMContentLoaded : load");
}
}, false);
} else {
// FALLBACK LOADER : If the readyState is late or unknown, go ahead and try and wait for a full page load event. Note: This function below was required for Internet Explorer 9-10 to work because of non-support of some readyState values! IE 4-9 only supports a "readyState" of "complete".
if (document.readyState === 'complete' || document.readyState === "loaded") {
Start("Browser Loader : Document : readyState : complete");
} else {
window.addEventListener('load', function () {
Start('Browser Loader : Window : Event : load');
}, false);
}
}
// If 'addEventListener' is not be supported in the browser, try the 'onreadystate' event. Some browsers like IE have poor support for 'addEventListener'.
} else {
// Note: document.onreadystatechange may have limited support in some browsers.
if (document.onreadystatechange) {
document.onreadystatechange = function () {
if (document.readyState === "complete" || document.readyState === "loaded"){
Start("Browser Loader : Document : onreadystatechange : complete");
}
// OPTIONAL: Because several versions of Internet Explorer do not support "interactive" or get flagged poorly, avoid this call when possible.
//else if (document.readyState === "interactive") {
//Start("Browser Loader : Document : onreadystatechange : interactive");
//}
}
} else {
// Note: Some browsers like IE 3-8 may need this more traditional version of the loading script if they fail to support "addeventlistener" or "onreadystate". "window.load" is a very old and very reliable page loader you should always fall back on.
window.onload = function() {
Start("Browser Loader : Window : window.onload (2)");
};
}
}
}
} else {
// LEGACY FALLBACK LOADER. If 'document.readyState' is not supported, use 'window.load'. It has wide support in very old browsers as well as all modern ones. Browsers Firefox 1-3.5, early Mozilla, Opera < 10.1, old Safari, and some IE browsers do not fully support 'readyState' or its values. "window.load" is a very old and very reliable page loader you should always fall back on.
window.onload = function () {
Start("Browser Loader : Window : window.onload (1)");
};
};
Note: When you run this script in a web browser, be sure to press F12 to pull up the browser tools screen and check the console tab to see the result. It will tell you at what stage the web page loader was triggered and when it called the 'Start()' script.
In most modern browsers (HTML5 or post-2010) it should be triggered as soon as the DOM or Document Object Model of HTML markup is rendered but the rest of the web page resources, CSS, images, video, and other files are still loading. In modern browsers this is usually between a readystate of "interactive" and "complete" and the DOM is built but the browser is still downloading other resource files. This allows your JavaScript to access and start manipulating the HTML tree or DOM very very early.
Older browsers like Internet Explorer v. 3-8 or Netscape, do not understand the advanced DOM checks so would require the full or "complete" load of the DOM and all page resources before calling your JavaScript.

Use JavaScript to prevent a later `<script>` tag from being evaluated?

This is a bit of an oddball use case, but I have my reasons:
I'd like to be able to write
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
in my markup and, using the code in first.js, prevent or delay the execution of second.js. Is this possible, in any browser? What if the contents of first.js are inlined? (If it helps, assume that the second script tag has an id attribute.)
Since I've gotten a couple of answers that missed what I'm getting at, I should clarify:
The solution must be entirely within first.js. Anything that require changes to the original HTML of the page, or to second.js, is not acceptable.
It is acceptable to load second.js via Ajax and execute it using eval. That's the easy part. The hard part is preventing the immediate execution of second.js.
Assume that you don't know what's in second.js. So, you can't just replace each global function called by second.js with a no-op function. (Plus, this would almost certainly lead to errors.)
If you know of a solution that works in some browsers but not in others, I'd love to hear it.
Example: To make this a little more concrete, let's say that the code
<script type="text/javascript">
function func() {
window.meaningOfLife = 42;
window.loadSecond();
};
setTimeout(func, 10);
</script>
precedes the two script includes, and that second.js contains the line
if (window.meaningOfLife !== 42) {throw new Error();}
first.js should be able to prevent this error by delaying second.js from executing until window.loadSecond is run. (Assume the implementation of window.loadSecond is also in first.js.) It is not allowed to touch window.meaningOfLife.
Update: Alohci's answer meets these requirements, but only on the condition that the second script tag comes immediately after the first, with nothing but whitespace in between. If someone could extend his hack to avoid that requirement, without introducing other unwanted consequences, that would be amazing...
Given your specific requirements set, this is actually quite simple and should work completely cross-browser. It does require however, that first.js immediately precedes second.js without anything between them except white space.
First, let's assume that the HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<title>Test Case</title>
<meta charset="UTF-8" />
<script type="text/javascript">
function func() {
window.meaningOfLife = 42;
window.loadSecond();
};
</script>
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
</head>
<body>
<p>Lorem ipsum dolor sit amet ...</p>
Run Func()
</body>
</html>
I've removed the setTimeout because that can cause func() to run before start.js runs causing a "loadSecond is not defined" error. Instead, I've provided an anchor to be clicked on to run func().
Second, let's assume that second.js looks like this:
document.body.appendChild(document.createTextNode("second.js has run. "));
if (window.meaningOfLife !== 42) {throw new Error();}
Here, I've just added a line to append some text to the document body, so that it is easier to see when second.js actually runs.
Then the solution for first.js is this:
function loadSecond()
{
var runSecond = document.createElement("script");
runSecond.setAttribute("src", "second.js");
document.body.appendChild(runSecond);
}
document.write("<script type='application/x-suppress'>");
The loadSecond function is just there to run second.js when func() runs.
The key to the solution is the document.write line. It will inject into the HTML <script type='application/x-suppress'> between the close script tag of first.js and the open script tag of second.js.
The parser will see this and start a new script element. Because the type attribute has a value which is not one that the browser recognises as being JavaScript, it will not attempt to run its content. (So there are an infinite number of possible type attribute values you could use here, but you must include a type attribute, as in its absence, the browser will assume that the script's content is JavaScript.)
The second.js script's opening tag will then be parsed as text content of the new script element and not executed. Finally the second.js script's closing tag will be re-purposed to close the new script element instead, which means that the remainder of the HTML is parsed correctly.
You can see a working version at http://www.alohci.net/static/jsprevent/jsprevent.htm
In first.js, set var shouldILoad = true
Then, load second.js this way:
<script>
if (shouldILoad) {
(function() {
var myscript = document.createElement('script');
myscript.type = 'text/javascript';
myscript.src = ('second.js');
var s = document.getElementById('myscript');
s.parentNode.insertBefore(myscript, s);
})();
}
</script>
(where 'myscript' is the ID of some element before which you'd like to insert the new Script element)
As far as I know, you can't. If the markup looks like
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
you can't access the second script element from within first.js, as it hasn't been added to the DOM at the moment the first script runs (even not if you assign an id to the second element). It doesn't matter whether the code of second.js is put inline or in an external file.
The only thing I don't understand is your second point. First you say that you can't control the markup of the document, but then you state it is possible to load second.js dynamically (using AJAX).
Following article describes the way you could block (3-rd party) scripts loading/execution from your script (including the both tag in the page head and dynamically added tags).
To handle existing tags on a page:
Use a MutationObserver to observe script elements insertion and inside the MutationObserver callback backup the script (to enable/insert it later) and change the script type to "javascript/blocked" (not works in IE, Edge, Firefox). Also you could handle deprecated (but working) beforescriptexecute event in Firefox to prevent script load.
Manually set type "javascript/blocked" (works everywhere including IE and Edge) like
<script type="text/javascript" type="javascript/blocked" src="second.js"></script>, then backup it in MutationObserver callback and re-add it later.
To handle dynamically added tags
Monkey-patch the document.createElement.
Override ‘src’ and ‘type’ descriptors on the HTMLScriptElement prototype.
Also this guys provide a yett library with the approach described in the article.
All <script> tags have their own execution context, which makes it nearly impossible to interfere with each other. Of course you've got the (infamous) global object (referenced by window in browsers).
Preventing the execution of second.js is rather simple: break it!
Assuming that second.js tries to call document.getElementById for example:
Working example of breaking jQuery, then loading later (with dependecies).
Tested on: IE 6+, FF 3.6+, Chrome
end of first.js
var execute;
// saving our position
var scripts = document.getElementsByTagName("script");
var i = scripts.length;
// breaking getElementById
var byId = document.getElementById;
document.getElementById = null;
var interval = setInterval(function () {
if (i != scripts.length) {
var second = scripts[i];
// stop polling
clearInterval(interval);
// fix getElementById
document.getElementById = byId;
// set the delayed callback
execute = function (onload) {
var script = document.createElement("script");
script.src = second.src;
script.onload = script.onreadystatechange = onload;
document.getElementsByTagName("head")[0].appendChild(script);
};
}
}, 100);
anytime you wanna execute second.js
execute(function(){
// second.js dependant code goes here...
});
Note: the onload parameter for execute is optional.
Just to see if this was possible, I had first.js send a synchronous XHR to a PHP file, and had the PHP file delete second.js. When the readyState reached '4', I had the JS alert something, to stop the thread. Then I went and checked the server... Yeah, second.js was deleted. And yet, it wouldn't work. I'd close the alert box, and the code that was in second.js would still be executed, despite the fact that the file was gone.
I don't really know what this means, but the answer to your question is probably, "No, it's not possible."
you may use setTimeout() to delay the execution of some code

Categories

Resources