I have a small script
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"
+ "ipt><script>alert($"+"().jquery);</scri" + "pt></head></html>");
But I get a $ is undefined in Internet Explorer. I think it tries to run the script before loading the library.
However this runs in Firefox. Please help.
Edit: I open a new window and write to that window's document.
What's wrong with creating the <script> tag the proper way? document.write is evil, end of discussion.
Try with this:
var load_script = function(options) {
options.owner_document = options.owner_document || document;
var script_tag = options.owner_document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', options.src);
script_tag.onload = function() {
script_tag.onreadystatechange = null;
options.callback && options.callback();
};
script_tag.onreadystatechange = function() {
if (script_tag.readyState == 'loaded' || script_tag.readyState == 'complete') {
script_tag.onload = null;
options.callback && options.callback();
}
};
options.owner_document.getElementsByTagName('head')[0].appendChild(script_tag);
};
as you see, there's a simple API on that snippet:
src - source of the script
owner_document - document where the script will be inserted, defaults to the current document where the script is running from
callback - function to run after the script has loaded, anything that requires the src script is safe to be run inside this closure.
example usage:
// sample loading of jQuery
load_script({
src: '/js/jquery-1.4.2.min.js',
callback: function() {
// jQuery is available at this point, run your code.
}
});
alternatively, you can use loaders like requiere.js and LABjs
The thing is that you have unintentionally triggered IE to load your scripts in a non-blocking way. Read this for more information: Loading JavaScript without blocking.
The page suggests that you use this code to get notified when the script has finished loading:
//Internet Explorer only
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "/js/jquery-1.4.2.min.js";
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
// Your code goes here.
alert("Script is ready!");
}
};
document.body.appendChild(script);
try running your second script on page load(window.onload) , or try inserting the script in the body, not in the head section.
try :
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"
+ "ipt><script>window.onload = function(){alert($"+"().jquery);};</scri" + "pt></head></html>");
the browser won't load the library instantly (unless it has it in its cache) so your script might get called before the library is loaded.
See this solution:
JavaScript's document.write Inline Script Execution Order
(also note the comment, it's important)
You could write a recursive function that waits for the jquery library to load and then executes any code once the library is loaded. You probably want to add a conditional that breaks out of the recursion if it gets too deep.
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"+ "ipt><script type='text/javascript'>var wait_for_jQuery = function() {if (typeof $ === 'undefined') {setTimeout('wait_for_jQuery()', 1000);} else {alert($"+"().jquery);}}; wait_for_jQuery(); </scri" + "pt></head></html>");
Working example here: http://www.jsfiddle.net/YqTgM/36/
Have you tried moving the script tag running your code to the end of the body tag? According to Yahoo's best practices (creators of YSlow):
The problem caused by scripts is that
they block parallel downloads. The
HTTP/1.1 specification suggests that
browsers download no more than two
components in parallel per hostname.
If you serve your images from multiple
hostnames, you can get more than two
downloads to occur in parallel. While
a script is downloading, however, the
browser won't start any other
downloads, even on different
hostnames.
So the result would look like...
document.write("<html><head><script src='/js/jquery-1.4.2.min.js' type='text/javascript'></scr"
+ "ipt></head><body><script>alert($"+"().jquery);</scri" + "pt></body></html>");
Although I would also advocate not using document.write().
Give this a try?
http://jsfiddle.net/MP75r/
If you want simplified conditional loading of jQuery, there are many options out there already; no need to reinvent the wheel. Already mentioned are RequireJS and LabJS. I am using Head.js and it couldn't be easier to accomplish the goal of non-blocking loading while respecting execution order.
you are just creating a tag to reference jQuery, and as soon as its created next statement is an alert. There is no way specified by you that alert statement should run after after file load.
A simple timer should do it.
(function(){
function wait() {
if (typeof jQuery == 'undefined') {
setTimeout(wait, 50);
} else {
run(); // your code to execute
}
}
wait();
})();
Example on jsfiddle: http://jsfiddle.net/madr/Gtafq/
Inside the <script></script> tag wrap your code in a document ready function so the libraries are loaded before running.
$(document).ready(function() {
// put all your jQuery code here
});
Related
I'm trying to build some small widgets tools that webmasters can embed in their websites.
Is there any way that the webmaster can simply load this tool by including a script like this?
<script src="http://mysite/widget.js"></script>
I tried to inject it by loading it in AJAX and then do an appendChild() on the body, it's working this way but the JS is not executed.
Content to be injected:
<div>one div</div>
<script>alert('some js')</script>
widget.js
function load(content) {
var $body = document.body,
$div = document.createElement("div");
$div.id = "widget";
$div.innerHTML = content;
$body.appendChild($div);
}
The content variable contains HTML and JS but the JS is not executed when injected.
Since you don't know where you script will be added - to the head or body tag - and consequently when it will be executed, you need to make sure that DOM is ready, so:
function load(content) {
if (document.readyState === "complete" || document.readyState === "loaded") {
// manipulate DOM
} else {
document.addEventListener('DOMContentLoaded', function() {
// manipulate DOM
})
}
Also, since scripts elements added using innerHTML don't get executed, you'd have to either use eval or better create a script like that:
var s = document.createElement('script');
s.text = "alert(\"hi\");"
document.getElementsByTagName('head')[0].appendChild(s);
However, there is no point to add <script> tag to execute some script in your template, since your script is already running when you're adding DOM so do all necessary setups there.
UPDATE
please see Maximus' answer.
Right, the default native innerHTML doesn't execute js for safe sake.
jQuery or zepto $.fn.append() can satisfy your need, here is the reason (from zepto source code)
if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
(!el.type || el.type === 'text/javascript') && !el.src)
window['eval'].call(window, el.innerHTML)
You can see in the function $.fn.append it will see if it's a script, if so it will use eval to run the content. You may need eval to, but be careful since some webmasters may deliver 'vice' code
I want to asynchronously include a specific version of jQuery in a page I don't control (e-commerce platform) and only use it in my script. The page may load other scripts, which may also want to do the same thing, and I don't know the order in which the page includes my script vs others.
Using jQuery.noConflict([removeAll]), can I ensure that:
my script gets the right version of jQuery
I don't overwrite jQuery version for anyone else?
I think this question is different from most other multiple jQuery version questions because people make assumptions about script inclusion order and don't use asynchronous jQuery loading with callbacks.
Thanks!
Here's my attempt at this (could someone confirm this is ok?):
myscript.js
(function() {
var myJQuery;
function loadjQuery(url, callback) {
var runCallbackOnSuccess = function() {
if (typeof jQuery != "undefined") {
myJQuery = jQuery.noConflict(true); // NOTICE THIS LINE
callback();
}
};
var scriptTag = document.createElement("script");
scriptTag.setAttribute("src", url);
scriptTag.onload = runCallbackOnSuccess;
document.getElementsByTagName("head")[0].appendChild(scriptTag);
}
function doSomethingInMyScript() {
myJQuery(document).ready(....);
}
loadjQuery("https://code.jquery.com/jquery-A.B.C.min.js", doSomethingInMyScript);
})();
otherscript.js (outside my control, but assuming it's like this)
(function() {
function loadjQuery(url, callback) {
<same as above, but without the noConflict() call>
}
function doSomethingInOtherScript() {
jQuery(document).ready(....);
}
loadjQuery("https://code.jquery.com/jquery-X.Y.Z.min.js", doSomethingInOtherScript);
})();
Will this code work regardless of:
whether the page includes myscript.js or otherscript.js first
whether the callback functions for myscript.js or otherscript.js is executed first?
I suspect it should be fine as long as the callback functions are executed immediately after each respective jQuery is loaded, with no possibility of code from the other script interleaving between jQuery loaded and callback executed.
I insert the jquery tag
var newScript = document.createElement("script");
newScript.id = "grazit_jq";
newScript.type = "text/javascript"
newScript.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js";
script.parentNode.appendChild(newScript);
And after that I want to check if jquery exists.
I do so in a few ways.One way is like this:
var checkJquery=function(){
if(typeof(jQuery) === 'undefined')
{
console.log('jQuery doesnt exist');
window.setTimeout(arguments.callee,1000);
return;
}
else{
sprk_lib=new sprk();
sprk_lib.load(jQuery);
}
};
sprk.prototype.load=function($){
console.log($);
};
UPDATE
Now i updated the code. It will call the function as soon as the jQuery object is availeable.
In that function I will execute my jquery code. Do you think that there will be problems? is it okay to do it that way?
original
I wait there. problem is that in the console log , it returns null and the prints jQuery exists. How can I stop the script till the jquery is loaded,, and then continue executing the script?
Consider using an established JavaScript script loader such as LABjs or LazyLoad which offer reliable callbacks that don't fire until the script has fully loaded.
Check out this summary of 5 JavaScript script loaders.
For example:
LazyLoad.js('https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js', function () {
console.log('jQuery exists!');
console.log('Document title: ' + $("title").text());
});
One point of note - if you want to hold off all further script execution until after jQuery has loaded, you could put the rest of your script inside this callback function. However, that would break a whole load of best practices (separation of concerns, loose coupling etc) so your best bet is to pass an existing function as your callback, which can in turn call out to other functions etc. For example:
function doSomething() {
console.log('I am doing something!')
doSomethingElse();
}
function doSomethingElse() {
console.log('I am doing something else!');
}
LazyLoad.js('https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js', doSomething);
You could further improve your application architecture (e.g. by introducing PubSub) but that's getting outside the scope of this question so I'll leave it there.
I'm trying to write a function which will append a javascript file to the DOM, but I am looking to have the rest of the code wait until the newly added JS file is completely loaded. Here is an example of what I am trying to accomplish, although this code doesn't work properly:
$(document).ready(function () {
var newScript = document.createElement("script");
newScript.setAttribute("type", "text/javascript");
newScript.src = "http://www.domain.com/script.js";
document.getElementsByTagName("body")[0].appendChild(newScript);
$(newScript).ready(function () { // This is the idea of what I'm trying to do, but this doesn't seem to actually wait until the new file is completely loaded.
foo.bar(); // foo is a new global variable which is declared in the newScript. This causes an error "foo is not defined".
// Here is where more code I wish to execute should continue.
});
});
As Musa mentioned in the comments above. Use jQuery's getScript and use the success callback function to trigger your other functions.
If you want more robust module loading functionality, then require.js works great in this capacity. Check out: http://requirejs.org/docs/why.html for an overview. I use require.js specifically for lazy-loading script modules.
Using jQuery (as you've tagged), it's extremely easy:
$.getScript('/script.js', function() {
foo.bar();
});
There's a few different ways to do this... via libraries or "by hand," so to speak, using only the browser APIs and straight JavaScript. For an answer on how to do this in JS only, look here for Stoyan's post to give you guidance. Basically, the gist of it is setting an event handler to both the script's unload and onreadystatechange properties and then check to see if the readyState is "loaded" or "complete" (if it exists at all). It would look something like this:
var done = false;
newScript.onload = newScript.onreadystatechange = function () {
if (!done && (!newScript.readyState || newScript.readyState === "loaded" || newScript.readyState === "complete)) {
done = true;
// run your actual code here
}
};
I'm having some concurrency issues with a webpage I'm building. Basically, I have three script files that I'm using to hold some data:
script1.js:
var myValue = 1;
script2.js:
var myValue = 2;
script3.js:
var myValue = 3;
And I have one page, mypage.html that basically looks like this:
<html>
<script>
function get_number()
{
var script_file = GetQueryStringValue( "run" );
e = document.createElement('script');
e.type='text/javascript';
e.src = script_file + ".js"
head.appendChild( e );
document.write( myValue );
}
</script>
<body onload="get_number()">
<div onclick="get_number()">Click me!</div>
</body>
</html>
The main idea with this page is that you would query it like this:
mypage.html?run=script1
which would tell the get_number() function to dynamically insert script1.js in to mypage.htm. Then, I call get_number() to display the value loaded from the script.
Now, I've stripped down the above to what I think are the relevant parts and I've left out a bunch of stuff, obviously. My actual code loads a large array of values and is more interesting... But, I'm hoping someone can help me out with this regardless.
What I'm finding is that in IE, the number displays correctly.
In Firefox, Chrome and Safari, I get an error that myValue is undefined. However, if I click the Click me div that I created, the number displays correctly. So, I know I'm correctly loading the javascript external file. But, it just isn't loaded in time for my get_number() function to work correctly onload.
Now, I hacked up my file a little so that the get_number function looks like this:
function get_number()
{
var script_file = GetQueryStringValue( "run" );
e = document.createElement('script');
e.type='text/javascript';
e.src = script_file + ".js"
head.appendChild( e );
setTimeout( function() { document.write( myValue ), 10 } );
}
The setTimeout delays enough for the DOM to be updated and the javascript to be loaded in most cases. But, this is a total hack. Firefox tends to load this 'improved' code correctly all the time while Chrome and Safari manage to get the value about 50% of the time.
So, I guess I'm wondering, is there a better way to accomplish what I'm trying to do here? It's very important that the value be driven externally (by the query string), but other than that requirement, I'm very flexible.
This method of dynamically loading script files using DOM methods like head.appendChild is asynchronous, meaning that the page does not wait for it to load before running any further code. If you did not want this asynchronous behaviour, you could load them with regular elements in your HTML, or you could mess around with a 'synchronous' XMLHttpRequest to grab it and eval() to run it.
You probably don't want to do that, though, because being asynchronous makes the entire page load faster, anyway. You will probably just need to add some logic that waits for the dynamically loaded script to have loaded before you go on with the next bit of code. This may, however, involve polling using setInterval() until you notice a variable from the included script has been defined. Or, you could add code in the included script that calls a method to whatever has been waiting for it.
Or, you could look at how jQuery does something similar, look for where the ajax method is defined, specifically these parts...
var script = document.createElement("script");
// then later ...
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
// do processing
head.removeChild( script );
}
};
// ...
head.appendChild(script);
Could you possibly add a function call to your script files, so that when they're run, they call the function that does your
document.write( myValue );
So that script1.js would be
var myValue = 1;
scriptLoaded();
and your main file would have:
function scriptLoaded(){
document.write( myValue );
}