I'm trying to reload javascript resources programmatically in Chrome and if I run that from console, then it works fine, but if I put that into the code to reload after an event was fired, then the resource doesn't change. What is the problem?
I use this reloader:
var docHeadObj = document.getElementsByTagName("head")[0];
var dynamicScript = document.createElement("script");
dynamicScript.type = "text/javascript";
dynamicScript.src = 'js/resource.js';
docHeadObj.appendChild(dynamicScript);
And here is the event handler:
obj.onclick(function(){
var docHeadObj = document.getElementsByTagName("head")[0];
var dynamicScript = document.createElement("script");
dynamicScript.type = "text/javascript";
dynamicScript.src = 'js/resource.js';
docHeadObj.appendChild(dynamicScript);
});
After both cases the new and proper <script> element is appended to the <head> and on network tab the resource is downloaded, but in case of the second one the resource is never change.
EDIT:
I'm closer to the problem. If I have this code in the resource which will be reloaded alert("aaa");
And I open the application then after performing a click event I get an alert with 3 'a' letter. Then I decrease the number of the letters to 2, reload the resource, perform a click event, then I again see 3 'a' letter. Then I decrease the number of letters to 1, save and click, then I see 2 'a' letter. So it seems to be the Chrome (and also FF) stores the last modified resource except if I reload that from console.
You should wait before the script is loaded before you use it. It can be achieved with using onload event for the dynamic script
function loadScript(url, callback) {
var docHeadObj = document.getElementsByTagName("head")[0];
var dynamicScript = document.createElement("script");
dynamicScript.type = "text/javascript";
dynamicScript.src = url;
// bind the event to the callback function
dynamicScript.onreadystatechange = callback; //for ie<9
dynamicScript.onload = callback;
// Fire the loading
docHeadObj.appendChild(dynamicScript);
}
var testJQuery = function () {
console.log($(this));
};
var obj = document.getElementById("test");
obj.onclick = function () {
loadScript("//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js", testJQuery);
};
Working example on fiddle
If you just delete the last instance of the script tag before reloading it, that should force it to reload.
function load(link, id) {
var exists = document.getElementById(id)
if(exists){
exists.parentNode.removeChild(exists);
}
var script = document.createElement('script');
script.src = link;
script.id = id;
document.body.appendChild(script);
}
obj.onclick = function(){
load('js/resource.js','dynamicScript')
}
Related
I have a toggle switch that when i press it adds a Javascript to the head of the page:
function yourcallback (){
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'dev/additional_script.js';
head.appendChild(script);
}
Now when i use firebug to debug it i can see all of the scripts however if i try to call a function to that script it tells me that the function i am trying to call is undefined.
So it would seem that the script is not loaded correctly.
HOWEVER if i in firebug try and call the same function it returns function()
meaning that it should be defined.
Has anyone experianced anything like this and / or know how i can fix it.
please tell me if you need more code information and il add it to my question
update
Okay so here is the thing i have two scripts:
The script that controls the switch button (when the user press it another script will be loaded)
the script that is loaded when the script button is pressed ( this is where the function is located.
In script one i am trying to call a function in script 2 after it has been loaded and this is where i am getting the error:
The way i am calling the function is just using the function name
The function i am trying to access looks like this:
var init = function() {
var i, anchors, scriptTag;
//get the uid...
scriptTag = document.getElementById("link-replacer");
if(scriptTag) {
uid = scriptTag.getAttribute("data-uid");
} else {
uid = "none";
}
urlpart = forwardUrl+"?uid="+uid+"&url=";
if(doOnLoadReplace) {
anchors = document.getElementsByTagName("a");
for(i = 0; i < anchors.length; ++i) {
fixHref(anchors[i]);
}
}
};
Try this code
var myScript = document.createElement('script');
myScript.setAttribute('type', 'text/javascript');
myScript.setAttribute('src', 'dev/additional_script.js');
var headFirst = document.getElementsByTagName('head');
headFirst[0].appendChild(myScript);
I think you're adding the script incorrectly. Try this:
var newJS = document.createElement('script');
newJS.setAttribute('type','text/javascript');
newJS.setAttribute('src', 'dev/additional_script.js');
Is there a way (event listener or otherwise) to detect when a particular external javascript is about to load / is loading / has finished loading?
In otherwords, does the browser fire an event when it's about to load, is loading, and/or has finished loading a particular external script?
For my purposes it's not enough to simply check to see if a known object exists or anything like that. Instead, I need something that will detect a JS file is loading/loaded regardless of the contents of the JS file.
The following example works in Chrome. It attaches an handler on the onload event of the head tag and then adds an external javascript file. When the file is loaded, the event is captured and an alert appears.
http://jsfiddle.net/francisfortier/uv9Fh/
window.onload = function() {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", "http://code.jquery.com/jquery-1.7.1.min.js");
head.addEventListener("load", function(event) {
if (event.target.nodeName === "SCRIPT")
{
alert("Script loaded: " + event.target.getAttribute("src"));
}
}, true);
head.appendChild(script);
}
Since all browsers blocks the "UI thread" when processing <script> tags, you can rely that pre-existing tags are loaded.
If you are loading a script dynamically, you can listen to the load event of the <script> tag.
function loadScript(src, callback) {
var script = document.createElement("script");
script.setAttribute("src", src);
script.addEventListener("load", callback);
document.getElementsByTagName("script")[0].insertBefore(script);
};
loadScript("http://code.jquery.com/jquery-1.7.1.min.js", function(){
alert("loading is done");
});
<script onload> will fire when a script is finished loading.
You will not be able to do something like:
<script src="/foo.js"></script>
<script src="/bar.js"></script>
<script>
function alertonload(src){
console.log(src+' is loaded');
}
scripts = document.getElementsByTagName('script');
for(var i=0; i<scripts.length; i++){
scripts[i].onload = function(){ alertonload(scripts[i].src); };
}
</script>
This is pure conjecture and speculation; I have not tried it and there's probably better ways to write it, but this will not do what you're looking to do. EDIT: The scripts are loaded as the browser sees them, not after the fact. They will be loaded before this occurs.
I wrote a script for those who wants to call a function after all external files (dynamically added) are loaded. It goes like this:
var file = [];
var loaded = [];
var head = document.getElementsByTagName('head')[0];
var fileOnLoad =
// Pass the arrays to your function
(function(file, loaded){ return function(){
loaded.push(true);
// Print the number of files loaded
document.getElementById("demo").innerHTML +=
"<br>"+loaded.length+" files loaded";
if(file.length == loaded.length){
alert("All files are loaded!");
}
}})(file, loaded);
////////////////////////////////////////////////
//// ////
//// Add the external files dynamically ////
//// ////
////////////////////////////////////////////////
file[0] = document.createElement('script');
file[0].src =
"https://maps.googleapis.com/maps/api/js?v=3.exp";
file[0].onload = fileOnLoad;
head.appendChild(file[0]);
file[1] = document.createElement('script');
file[1].src =
"http://code.jquery.com/jquery-latest.js";
file[1].onload = fileOnLoad;
head.appendChild(file[1]);
file[2] = document.createElement('script');
file[2].src =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js";
file[2].onload = fileOnLoad;
head.appendChild(file[2]);
file[3] = document.createElement('link');
file[3].rel = "stylesheet";
file[3].href =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css";
file[3].onload = fileOnLoad;
head.appendChild(file[3]);
file[4] = document.createElement('link');
file[4].rel = "stylesheet";
file[4].href =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css";
file[4].onload = fileOnLoad;
head.appendChild(file[4]);
<div id="demo">0 file loaded</div>
Hope it helps!
If you can use jQuery, try $.getScript(). Docs here.
I know that IE doesn't have a load event for <script> elements — is there any way to make up for that reliably?
I've seen some talk of things (e.g., requestState == "complete") but nothing very verifiable.
This is to be used so that code can be called after a script is finished loading, so that I don't have to use AJAX to load new sources (thus eliminating issues with cross-domain AJAX).
You can use a script loader like head.js. It has its own load callback and it will decrease load time too.
From the headjs code: (slightly modified to be more portable)
function scriptTag(src, callback) {
var s = document.createElement('script');
s.type = 'text/' + (src.type || 'javascript');
s.src = src.src || src;
s.async = false;
s.onreadystatechange = s.onload = function () {
var state = s.readyState;
if (!callback.done && (!state || /loaded|complete/.test(state))) {
callback.done = true;
callback();
}
};
// use body if available. more safe in IE
(document.body || head).appendChild(s);
}
I want to add that if you don't support IE7 and below, you don't need onreadystatechange stuff. Source: quircksmode.org
Simplified and working code from original answer:
function loadScript(src, callback) {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = src;
s.async = false;
if(callback) {
s.onload = callback;
}
document.body.appendChild(s);
}
This is just an extension of ilia's answer. I used scriptTag like this: Works great:
// these 3 scripts load serially.
scriptTag(boot_config.DEPENDENCIES.jquery,function(){
// jquery ready - set a flag
scriptTag(boot_config.DEPENDENCIES.jqueryui,function(){
// jqueryui ready - set a flag
scriptTag(boot_config.DEPENDENCIES.your_app,function(){
// your_app is ready! - set a flag
});
});
});
// these 2 scripts load in paralell to the group above
scriptTag(boot_config.EXTERNALS.crypto,function(){
// crypto ready - set a flag
});
scriptTag(boot_config.EXTERNALS.cropper,function(){
// cropper ready - set a flag
});
Problem:
Load js files asynchronously, then check to see if the dom is loaded before the callback from loading the files is executed.
edit: We do not use jQuery; we use Prototype.
edit: added more comments to the code example.
I am trying to load all of my js files asynchronously so as to keep them from blocking the rest of the page. But when the scripts load and the callback is called, I need to know if the DOM has been loaded or not, so I know how to structure the callback. See below:
//load asynchronously
(function(){
var e = document.createElement('script');
e.type = "text/javascript";
e.async = true;
e.src = srcstr;
// a little magic to make the callback happen
if(navigator.userAgent.indexOf("Opera")){
e.text = "initPage();";
}else if(navigator.userAgent.indexOf("MSIE")){
e.onreadystatechange = initPage;
}else{
e.innerHTML = "initPage();";
}
// attach the file to the document
document.getElementsByTagName('head')[0].appendChild(e);
})();
initPageHelper = function(){
//requires DOM be loaded
}
initPage = function(){
if(domLoaded){ // if dom is already loaded, just call the function
initPageHelper();
}else{ //if dom is not loaded, attach the function to be run when it does load
document.observe("dom:loaded", initPageHelper);
}
}
The callback gets called properly due to some magic behind the scenes that you can learn about from this Google talk: http://www.youtube.com/watch?v=52gL93S3usU&feature=related
What's the easiest, cross-browser method for asking if the DOM has loaded already?
EDIT
Here's the full solution I went with.
I included prototype and the asynchronous script loader using the normal method. Life is just so much easier with prototype, so I'm willing to block for that script.
<script type="text/javascript" src="prototype/prototype.js"></script>
<script type="text/javascript" src="asyncLoader.js"></script>
And actually, in my code I minified the two files above and put them together into one file to minimize transfer time and http requests.
Then I define what I want to run when the DOM loads, and then call the function to load the other scripts.
<script type="text/javascript">
initPage = function(){
...
}
</script>
<script type="text/javascript">
loadScriptAsync("scriptaculous/scriptaculous.js", initPage);
loadScriptAsync("scriptaculous/effects.js", initPage);
loadScriptAsync("scriptaculous/controls.js", initPage);
...
loadScriptAsync("mypage.js", initPage);
</script>
Likewise, the requests above are actually compressed into one httpRequest using a minifier. They are left separate here for readability. There is a snippet at the bottom of this post showing what the code looks like with the minifier.
The code for asyncLoader.js is the following:
/**
* Allows you to load js files asynchronously, with a callback that can be
* called immediately after the script loads, OR after the script loads and
* after the DOM is loaded.
*
* Prototype.js must be loaded first.
*
* For best results, create a regular script tag that calls a minified, combined
* file that contains Prototype.js, and this file. Then all subsequent scripts
* should be loaded using this function.
*
*/
var onload_queue = [];
var dom_loaded = false;
function loadScriptAsync(src, callback, run_immediately) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
if("undefined" != typeof callback){
script.onload = function() {
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
script.onreadystatechange = function() {
if (script.readyState == 'complete'){
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
}else if(script.readyState == 'loaded'){
eval(script);
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
}
};
}
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
document.observe("dom:loaded", function(){
dom_loaded = true;
var len = onload_queue.length;
for (var i = 0; i < len; i++) {
onload_queue[i]();
}
onload_queue = null;
});
I added the option to run a script immediately, if you have scripts that don't rely on the page DOM being fully loaded.
The minified requests actually look like:
<script type="text/javascript" src="/min/?b=javascript/lib&f=prototype/prototype.js,asyncLoader.js"></script>
<script type="text/javascript"> initPage = function(e){...}</script>
<script type="text/javascript">
srcstr = "/min/?f=<?=implode(',', $js_files)?>";
loadScriptAsync(srcstr, initPage);
</script>
They are using the plugin from: [http://code.google.com/p/minify/][1]
What you need is a simple queue of onload functions. Also please avoid browser sniffing as it is unstable and not future proof. For full source code see the [Demo]
var onload_queue = [];
var dom_loaded = false;
function loadScriptAsync(src, callback) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
script.onload = script.onreadystatechange = function() {
if (dom_loaded)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
function domLoaded() {
dom_loaded = true;
var len = onload_queue.length;
for (var i = 0; i < len; i++) {
onload_queue[i]();
}
onload_queue = null;
};
// Dean's dom:loaded code goes here
// do stuff
domLoaded();
Test usage
loadScriptAsync(
"http://code.jquery.com/jquery-1.4.4.js",
function() {
alert("script has been loaded");
}
);
You can always put your initial loader script at the bottom, right before the closing body tag.
I am developing a bookmarklet that requires a specific version of jQuery be loaded on the page. When I have to dynamically insert a jQuery script tag to meet the requirments of the bookmarklet I want to wait for the onload or onreadystatechange event on the script tag before executing any function that requires jQuery.
For some reason the onload and/or onreadystatechange events do not fire. Any ideas on what I am doing wrong here?
var tag = document.createElement("script");
tag.type = "text/javascript";
tag.src = "http://ajax.microsoft.com/ajax/jquery/jquery-" + version + ".min.js";
tag.onload = tag.onreadystatechange = function () {
__log("info", "test");
__log("info", this.readyState);
};
document.getElementsByTagName('head')[0].appendChild(tag);
The FULL code: http://gist.github.com/405215
Probably too late for you, but here's how I got round the problem.
Basically, it collects the "$(document).ready(function(){})" and "$(function(){})" calls and runs them after jQuery has finished loading.
Instead of using onLoad, we use setInterval to wait for the variable jQuery to become a function after adding the script tag to <head>
var $_i, $_r = [];
var $ = function(func){
if(typeof(func) == 'function'){ $_r.push(func); }
return{ ready: function(func){ $_r.push(func); } }
}
window.onload = function(){
var s = document.createElement('script');
s.setAttribute('type', 'text/javascript');
s.setAttribute('src', 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js');
document.getElementsByTagName("head")[0].appendChild(s);
$_i = setInterval(function(){
if(typeof(jQuery) == 'function'){
clearInterval($_i);
for(var i in $_r){
$_r[i]();
}
}
}, 100);
}
if you use both events (tag.onload = tag.onreadystatechange), on IE9 it will be called two times.
You could always cheat, and put your onload/onreadystatechange logic into a setTimeout(), specifying a duration of 1 ms. Since you're inserting the script element into the DOM, you're guaranteed to see it execute before the timeout.
Example code:
var tag = document.createElement("script");
tag.type = "text/javascript";
tag.src = "http://ajax.microsoft.com/ajax/jquery/jquery-" + version + ".min.js";
document.getElementsByTagName('head')[0].appendChild(tag);
setTimeout(function(){
__log("info", "test");
}, 1);