I am new to a project in mature stage. There are many JS scripts and libraries on one of the pages, jQuery is used intensively. There is a weird error in IE7/8 only: somewhere in the middle of the page scripts execution jQuery object becomes undefined, so everything else stops working.
There are two scripts specified in the middle of HTML, say, like this:
<script src="script1.js"></script>
<script src="script2.js"></script>
In the end of the script1 a variable called jQuery contains an object as expected, but in the beginning of the script2 variable jQuery is already undefined. What happens in between?
Please, any ideas where to look and how to debug!
Is there a way to control the consequency of script execusion and debug the WHOLE consequent js (including all js includes) on one page?
Thank you! :)
UPDATE. I made a patch: in the end of the script1 I backup the variable:
var jQueryBackup = jQuery;
In the beginning of the script2 I restore it:
if (!jQuery && jQueryBackup) jQuery = jQueryBackup;
It made the script work, but does not explain how could a variable become undefined in between two scripts (I see it as literally between two lines of code).
Your <script> in the middle of the HTML ? Try to put them into the <head>
<head>
<script src="script1.js"></script>
<script src="script2.js"></script>
</head>
if you want to debug , this what i would do :
$.getScript("script1.js").done(function(script, textStatus) {
console.log('script1 is loaded');
$.getScript("script2.js").done(function(s,t){
console.log('script1.js is loaded');
});
})
.fail(function(jqxhr, settings, exception) {
console.log( "Triggered ajaxError handler." );
});
now you can debug it , you can also use native javascript and use onload.
if you need example , let me know.
example : ( this solution will work for IE8+, firefox, chrome )
function handleOnLoad() {
// do the same here for the script.2 file
}
function IEhandleOnLoad() {
if (this.readyState === 'complete' || this.readyState === 'loaded') {
handleOnLoad();
}
}
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.onreadystatechange = IEhandleOnLoad;
s.onload = handleOnLoad;
s.src = 'script1.js';
Related
Let's say I have some simple Javascript like:
<script>
var hello = function(){
alert("Hello World!");
}
</script>
.. on a page helloworld.html. If I loaded this script block into another page using Pjax. How do I execute the function hello()?
For security reasons, many browsers will not run Javascript injected by innerHTML, which I'm thinking Pjax likely uses. (Here's a minimal example.)
Maybe the solution proposed in Pjax's issue #48 will help
What worked for me was to place my jQuery code in a function, call it
normally on document.ready (for non-pushState browsers), and then bind
the function to pjax:end, i.e.:
$('body').bind 'pjax:end', mainFunction
This is possible with PJAX. You just need to have the script tag with type text/javascript.
Code from PJAX library:
function executeScriptTags(scripts) {
if (!scripts) return
var existingScripts = $('script[src]')
scripts.each(function() {
var src = this.src
var matchedScripts = existingScripts.filter(function() {
return this.src === src
})
if (matchedScripts.length) {
matchedScripts.remove();
}
console.error("FOUND SCRIPTS", scripts, matchedScripts.length);
var script = document.createElement('script')
script.type = $(this).attr('type')
script.src = $(this).attr('src')
document.head.appendChild(script)
})
}
I want to mimick this behavior:
<script src="console.log.1.js"></script>
<script>console.log(2)</script>
<script>console.log(3)</script>
That logs out:
1
2
3
Doing this doesn't work:
<script>
var x = document.createElement("script");
x.src = "console.log.1.js";
x.async = false;
x.defer = false;
document.body.appendChild(x);
console.log("2");
console.log("3");
</script>
It logs out:
2
3
1
The only way I found so far to achieve it:
<script>
document.write("<scrip" + "t src='console.log.1.js'></scrip" + "t>");
</script>
<script>
console.log("2");
console.log("3");
</script>
Is that really the only way to force synchronous loading of external scripts in all browsers? Why doesn't setting async=false, defer=false work?
UPDATE
FYI, if someone is wondering, the following document.write inception works (in Chrome..):
<script>
// http://jsbin.com/avatiw logs "during"
document.write('<scrip' + 't>console.log("before");document.write("<scrip" + "t src=\\"http://jsbin.com/avatiw\\"></scrip" + "t>");</scrip' + 't>');
document.write('<scrip' + 't>console.log("after");</scrip' + 't>');
</script>
Works and logs out:
"before"
"during"
"after"
Yes, that's the only way to force the script to load during the page parsing. Or at least, the only way I'd be willing to believe worked well cross-browser.
If your script were like this, I could see your thinking:
<script>
var x = document.createElement("script");
x.src = "console.log.1.js";
x.async = false;
x.defer = false;
document.body.appendChild(x);
</script>
<script><!-- note the new script element -->
console.log("2");
console.log("3");
</script>
...because in theory, when the parser hits the script element, it suspends everything (because there might be document.write statements) and calls into the JavaScript layer. So you might think, well, adding a script element to the end of the body at that point would insert it between the two.
But adding a script element via appendChild is just fundamentally different, it's by nature an asynchronous operation (your code continues while the script is being downloaded, which is not the case with script elements in the markup barring the defer or async attributes). I can't point at any one spec to say why, but the behavior you're seeing is exactly what I'd expect. The treatment of script elements inline with the markup is a bit special.
We can see that it's the download that's the issue — at least in Chrome — by comparing the result with using a script element with inline content.
Using an external file (live copy | live source):
<script>
console.log("before");
(function() {
var s = document.createElement("script");
s.src = "http://jsbin.com/avatiw"; // Logs the word "during"
document.body.appendChild(s);
})();
</script>
<script>
console.log("after");
</script>
Result:
before
after
during
Using inline script (live copy | live source — note that I've made no attempt to make this cross-browser, it works in Chrome and Firefox as they support the text property on script elements):
<script>
console.log("before");
(function() {
var s = document.createElement("script");
s.text = "console.log('during');";
document.body.appendChild(s);
})();
</script>
<script>
console.log("after");
</script>
Output:
before
during
after
I actually found that using a LazyLoad plugin was perfect for this use case, i.e.
if (typeof jQuery === 'undefined')
LazyLoad.js('//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js', function() {
initialize();
});
else
initialize();
I have 2 script files that you put in the <head>
However, they share the same Util script file.
I want that in case they are both loaded on the same page, the util file will load only once.
If only one of them is loaded, the util will still be loaded once...
I cant use <script src=...utils.js>... only the 2 scripts
I am using
var s = document.createElement("script");
s.src = s3 + "/js_enc/utils.js";
s.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(s);
What is the best way to achieve this?
thanks
This doesn't seem like a problem, since the Util script will only be loaded when it is declared:
<script type='text/javascript' src='util.js'></script>
<script type='text/javascript' src='script1.js'></script>
<script type='text/javascript' src='script2.js'></script>
But if you want to check that the script has been loaded, you could set a global variable in the Util script, something like
var utilFound = true;
Then check in each script to see if it is set or not.
if(!utilFound) { alert("Util not loaded!"); }
You could also toggle the variable depending on some condition, for example:
if(utilFound) {
alert("Util hasn't been accessed yet.");
utilFound = false;
}
else {
alert("Util has already been accessed.");
}
UPDATE
As per your edit, the "check if set and toggle" solution would work fine. If the variable has been set, don't run your document.createElement code.
In your util.js file, add
var utilLoaded = false;
Then in each script, add your snippet, plus a utilLoaded check/toggle:
if(!utilLoaded) {
var s = document.createElement("script");
....
utilLoaded = true;
}
My website dynamically embeds an external Javascript file into the head tag. The external Javascript defines a global variable myString = "data". At what point does myString become accessible to Javascript within the website?
<html>
<head>
<script type="text/javascript">
myString = null;
external = document.createElement("script");
//externalScript.js is one line, containing the following:
//myString = "data";
external.setAttribute("src", "externalScript.js");
external.setAttribute("type", "text/javascript");
document.getElementsByTagName("head")[0].append(external);
alert(myString);
<script>
</head>
<body>
</body>
</html>
This code alerts null (when I thought it would alert "data") in Chrome and IE, even though the DOM has loaded in externalScript.js at this point. When is externalScript.js actually evaluated by the browser and at what point do I have access to the new value of myString?
You can access it when the onload event fires for that script element, like this:
external = document.createElement("script");
external.onload = function() { alert(myString); };
script.onreadystatechange= function () { //for IE, the special kid...
if (this.readyState == 'complete') alert(myString);
}
external.setAttribute("src", "externalScript.js");
external.setAttribute("type", "text/javascript");
document.getElementsByTagName("head")[0].appendChild(external);
This just attaches a function to run once that script has loaded, execute any code that depends upon the script in there. Correction to previous answer: as seanmonstar points out in comments (thanks!), you do indeed need an IE exception here again, because it's a bit "special"...
My problem is that I need to dynamically include a javascript file from another external javascript file. I'm trying to do it by using this function:
function addCustomScriptTag(url) {
var scriptTag=document.createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.src=url;
var myElement = document.getElementsByTagName("head")[0];
myElement.appendChild(scriptTag);
}
The problem happens only in IE6 where trying to append to the head element causes an 'operation aborted' error.
Any help would be appreciated
It depends when you add it to the head DOM element. Operation aborted occurs in all versions of IE because you're trying to modify a DOM element via JavaScript before that DOM element has finished loading, http://support.microsoft.com/default.aspx/kb/927917.
If you need this script loaded right away, you could do an old school document.write to add the script tag, e.g.
<head>
<script>document.write('<script src='yourUrl.js'><\/scr'+'ipt>');</script>
</head>
Otherwise call your function in the body onload via plain old JavaScript or via a framework like jQuery a la document.ready.
Consider using a library like jQuery and then just use the equivalent (if not using jQuery) of getScript. This will handle cross-browser quirks and inconsistencies for the most part.
Append it to the body then. Javascript doesn't have to go exclusively in the <head> of your document.
I steal from the jQuery source:
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
success();
complete();
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
head.removeChild( script );
}
};
head.appendChild(script);
I think it's because IE6 doesn't support getElementsByTagName(), try replacing it with document.body.