I'm loading an external script that uses callback function, which returns some specific data. If this data is not received error should be displayed.
Here is the code I've made:
<script>
//setting initial state so that function will only work once
var visitors_loaded=false;
var my_callback = function( data ) {
if (visitors_loaded) return 0;
if (data) {
//success: callback function is called and it has a proper data
visitors_loaded=true;
alert(JSON.stringify(data));
}
else alert ('error'); //something went wrong
};
</script>
<script onload="my_callback(null)" onerror="my_callback(null)"
src="https://api.clicky.com/api/stats/4?site_id=32020&sitekey=9a19b1a4d1171193&type=visitors&date=this-month&output=json&json_callback=my_callback"></script>
As you can see... many things that can go wrong with the script, so I naturally added an onerror event. This on error event actually fires if you change host name or domain of the script to something non-existent.
However, if you only make changes to the url of the script, it can still connects to the server and fires an onload event instead. My callback function will not be called for those invalid requests, so I added an onload handler as well.
Now the problem is, if all loaded normally and data was returned, it will fire both, callback function and onload. I have noticed that callback function is triggered before the onload and set the visitors_loaded variable so that the handler function is only called once.
So far it works perfectly in JS fiddle and my offline site but I wonder if this is an expected behavior? Will that json_callback function always have precedence before the onload handler?
https://jsfiddle.net/5sfk9ht5/4/
Will that json_callback function always have precedence before the onload handler?
If the external script calls my_callback synchronously then yes.
The scripting section in the official html5 specification describes how these things are supposed to work. The specification is quite general and has to deal with a lot of details conserning encoding, CORS, ignore-destructive-writes counter and so on. But for this question we don't care about these specifics.
In step 4 there is a note:
Note: This is where the script is compiled and actually executed.
And in step 7 the load event is fired:
fire a simple event named load at the script element.
So the specification defines that the load event is always fired after the script has been executed.
As you see the specification also tells us why the onerror event is not fired if you change the URL. The error event is only created if loading the script fails. But all requests to https://api.clicky.com/api/stats/ return a HTTP 200 status. The invalid URLs return XML and thus a SyntaxError is thrown. But this does not cause the onerror handler to be triggered.
As others have mentioned if the callback is called asynchronously they can call your callback after the onload event. But I don't see a reason why they would do this async in your external script.
onload in older IE might not work for you 'onload' handler for 'script' tag in internet explorer, if you want to run all browsers you might need something like this https://jsfiddle.net/e287523t/2/ and should work for all
function addScript(fileSrc, callback) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.onreadystatechange = function() {
if (this.readyState == 'complete') {
callback();
}
}
script.onload = callback;
script.src = fileSrc;
head.appendChild(script);
}
Then the rest is defining my_callback and calling addScript
my_callback = function(someData) {
alert(JSON.stringify(someData));
};
Now the problem is, if all loaded normally and data was returned, it will fire both, callback function and onload. I have noticed that callback function is triggered before the onload and set the visitors_loaded variable so that the handler function is only called once.
This is because the callback is launched from within the called script from api.clicky.com
So far it works perfectly in JS fiddle and my offline site but I wonder if this is an expected behavior?
I see what you are getting at, a related question about what happens when a script fails is here, but I did some tests for you and here are the results.
tester.html:
<script>
var onLoadTest = function() {
console.log("Onload Called!");
};
var callbacktest = function() {
console.log("Called from other script!");
};
var errortest = function() {
console.log("Callback OnError!");
};
</script>
<script onload="onLoadTest()" onerror="errortest()"
src="script.js"></script>
script.js:
function otherScriptFunc()
{
//call the function in the original script
callbacktest()
}
otherScriptFunc(); // first call to other script
setTimeout(otherScriptFunc, 0); // final call to other script
Results from the console log
Called from other script!
Onload Called!
Called from other script!
Your OnLoad will be called when the JS in the other place has finished being parsed (async functions will do their own thing). For example, otherScriptFunc(); will call before onload but setTimeout(otherScriptFunc, 0); will be called after onload
Your OnError will only be called if there is a GET request error. IE, the file cannot be found, or URL cannot be resolved - nothing about what is in the file. (I tested it separately, just mess with the file name)
Your callback passed to the other script could be called whenever the other script feels like it. It has a reference to it and could decide to hold onto it for a little while and call it later after it has played around. Which means it could be in an async call waiting for data elsewhere. Which means, theoretically, your onload could in fact be called before the callback, but it depends on the other script and there is not a lot you can do about it.
Will that json_callback function always have precedence before the onload handler?
It's not about precedence, it is just dependent upon when the other script decides to call it.
Related
I am facing an issue where a jQuery function is not being called even though the proxy has successfully loaded (from what I can tell). The call we are making works fine on first load, but when we try to load this script in via AJAX it calls the required $.proxy() to the Initialise function, but then doesn't actually call that function.
The dynamic loaded in code is:
<script language="JavaScript"><!--
var ctrl = new VideoControl({"Id":"bc1093c8290a4037846c2052695a7d3a"}, "...");
//-->
</script>
And the javascript to create the object is:
function VideoControl(controlIds, videoMarkUp) {
this.controlIds = controlIds;
this.videoMarkUp = videoMarkUp;
var thisControl = this;
$(function () { $.proxy(thisControl.Initialise(), thisControl); });
}
VideoControl.prototype.Initialise = function () {
// do stuff
}
So the main function is called, but the Initialise() is not called when this is loaded in via AJAX controls in Chrome or IE...this does however work in firefox.
There is a stackoverflow answer which explains why $function is not being called but how do I get it to a point it will call (similar to how firefox deals with it)
Is anyone aware of something that firefox does differently when working with jQuery vs the other browsers?
There are no errors showing on the chrome developer tools, is there anywhere else that could be looked at in order to diagnose this?
Thank you in advance.
So the main function is called, but the Initialise() is not called
when this is loaded in via AJAX
$(function() {}) is alias for .ready() ; handlers for .ready() should be called at most once . AJAX appear to occur after document is loaded ? , where .ready() has previously called , $.isReady set to true preventing handlers within subsequent .ready() or $(function(){}) from being called.
Try removing $(function() {}) wrapper surrounding
function VideoControl(controlIds, videoMarkUp) {
this.controlIds = controlIds;
this.videoMarkUp = videoMarkUp;
var thisControl = this;
$.proxy(thisControl.Initialise, thisControl);
}
use
$(document).ready(VideoControl)
Though , not certain why $.proxy is used here ? , as the context of thisControl.Initialise not appear to be changed to a different context : this ?
Note also that js at Question thisControl.Initialise() called function; where function should be referenced jQuery.proxy( function, context [, additionalArguments ] )
I am a little confused by the following block of code. I will comment next to each line what I think it means. If someone could help me clarify any misunderstandings I have or confirm that I am in fact interpreting it correctly, I would very much appreciate that.
Here is this code in context: http://jsfiddle.net/MddHtt13/EMBZr/1/
if(!window.onload) { // If the window is not loaded then...
window.onload = function() { //Assign an anonymous function to the onload event
onLoad(); //Which, upon execution of the onload event execute the onLoad function
};
}
else { //This is probably the most confusing part
var oldWindowLoadFunction = window.onload; //Else if the window is loaded, assign the onload event to the variable oldWindowLoadFunction
window.onload = function() { //Then upon completion of the onload event, assign an anonymous function
oldWindowLoadFunction(); //which then re-executes the onload event
onLoad(); //and then executes the onLoad function
};
}
The first thing I don't understand is the exclamation point next to window.onload
if(!window.onload)
Why would I need to specify if the window is not yet loaded? Wouldn't I only want to attach the onLoad() function to the onload event so that upon completion it fires? Say with something like:
window.onload = onLoad();
Why the extra steps? Why the if/else statement?
Secondly, why in the second half of the code block do I need to reload the page only to reattach the onLoad() function again? That sort of brings me back to what I just asked. Why does it have to be more complicated that simply writing:
window.onload = onLoad();
Ofcourse when I change the code to be a simple statement, like the one above, it doesn't actually work. However, I still don't completely understand the necessity of each part of the code block in question.
If someone could walk me through this in detail it would be extremely helpful.
Edit:
Thanks to the help of the folks below I replaced all of this code with one simple statement:
window.addEventListener('load', onLoad);
The ! is a boolean inversion: if not window.onload is null or undefined, or in plainer English, if the variable named onload in the object named window is not null or undefined.
The logic basically says, if there is no onload function install mine. If there is an onload function install a new wrapper function which calls the existing function and then calls mine.
None of the code "reloads" the page. You are confusing the assignment of the onload handler with loading the page.
What the function is doing is adding onload functionality to a window object which may already have onload functionality by chaining added function to the original, but only if that's necessary.
In today's world, it's all redundant, since you can just add an event listener to a list of functions to be executed for the onload event.
if (!window.onload)
This is checking to see if window.onload is not null or undefined. If window.onload is already defined elsewhere then you might not want to replace it with your onLoad() function.
Your block of code basically checks to see if window.onload is defined elsewhere. If it isn't, assign onLoad() to window.onload. If it does, execute the existing window.onload and then call your onLoad() function as well.
In the following code, the function writeMessage is called without parenthesis. However it works fine but Is it a correct way of function calling in javaScript or its better to use parenthesis along with writeMessage().
window.onload = writeMessage;
function writeMessage()
{
document.write("Hello World");
}
window.onload = writeMessage; is not a call - it's an assignment. You assign the writeMessage function as the onload field of the window object. The actual call is performed (internally) as window.onload() which is equivalent to writeMessage() in your case.
In the following code, the function writeMessage is called without parenthesis.
Actually, it isn't. The code
window.onload = writeMessage;
does not call the function. It assigns the function to the onload property of window. Part of the process of loading the page in browsers is to fire the function assigned to that property (if any) once the loading process is complete.
If you wrote
window.onload = writeMessage();
what you'd be doing is calling writeMessage and assigning the result of the call to window.onload, just like x = foo();.
Note that the code you've actually quoted, which executes a document.write when the page loads, will wipe out the page that just loaded and replace it with the text "Hello world", because when you call document.write after the page load is complete, it implies document.open, which clears the page. (Try it here; source code here.) In modern web pages and apps, you almost never use document.write, but in the rare cases where you do, it must be in code that runs as the page is being loaded (e.g., not later).
the () is used to EXECUTE the function
when you write
window.onload = writeMessage;
you actually set a delegate ( pointer to a function to be executed) for which - when the onload event will occour.
That's correct already.
You don't need parenthesis because you're just storing the function in window.onload, not calling it yourself.
How do I clear out anonymous functions that are set to trigger via a jQuery document.ready() call?
For example:
<script type="text/javascript">
//some code sets a doc ready callback
$(document).ready(function ()
{
alert('ready');
});
//my attempt to prevent the callback from happening
window.onload = null;
$(document).unbind("ready");
</script>
The alert happens regardless of my attempts to circumvent it. Is there any way to do this?
You'd probably get the most appropriate answer if you described what problem you're really trying to solve.
jQuery doesn't have a publicly documented way to undo or block document.ready() handlers. If you control the code, you can use a global variable and a conditional like this:
var skipReady = false;
$(document).ready(function ()
{
if (!skipReady) {
alert('ready');
}
});
// skip the document.ready code, if it hasn't already fired
skipReady = true;
Or, if you want to hack into jQuery a bit (beyond the documented interfaces), you can do this:
$(document).ready(function() {
alert("ready");
});
// stop the ready handler
$.isReady = true;
You can see this last one work here: http://jsfiddle.net/jfriend00/ZjH2k/. This works because jQuery uses the property: $.isReady to keep track of whether it has already fired the ready handlers or not. Setting it to true makes it think it has already fired them so it won't every do it again.
This works:
$(document).bind("ready", function () { alert("hey!"); });
$(document).unbind("ready");
Seems like a bug to me - all other events in jQuery are able to be unbound. Omitting this one is inconsistent.
Not a direct answer as to the omission, but here's some related info from jQuery docs:
All three of the following syntaxes are equivalent:
$(document).ready(handler)
$().ready(handler) (this is not recommended)
$(handler)
There is also $(document).bind("ready", handler). This behaves similarly to the ready method but with one exception: If the ready event has already fired and you try to .bind("ready") the bound handler will not be executed. Ready handlers bound this way are executed after any bound by the other three methods above.
$(document).ready() is dependent on the onLoad event which is triggered by the browser meaning you can not prevent it from happening. If the alert() is determined by some condition then I would use an if/else statement to decide whether it is called.
Super old question, but came across the need to do this recently to prevent document.ready code I didn't control from running in certain instances. This can be achieved by proxying jQuery's ready function, rather like a test spy. The following will work:
var ready = $.prototype.ready;
// proxy the ready function
$.prototype.ready = function ( fn, allowed ) {
allowed = allowed || false;
if ( allowed ) {
ready.call( this, fn );
}
};
All calls to $( document ).ready will now be ignored. You can override this behaviour by passing true as the second argument: $( document ).ready( fn, true )
I was implementing a on-demand script controller based on jquery's getscript, it looks like this:
function controller = function(){
var script = function(){
var scripts = {};
return {
load: function(jsurl){
$.getScript(jsurl, null);
},
run: function(js){
window[js].apply(this,null);
}
}
};
return {
script: script()
};
}
var ctlr = controller();
then here is a remote script with a function to be loaded - remote.js
function remotefunc(){
alert( 'remotefunc invoked' );
}
and here is how the whole thing supposed to work, in the main script:
ctlr.script.load( 'remote.js' ); // remote script successfully loaded
ctlr.script.run( 'remotefunc' ); // got an error, window['remotefunc'] undefined
but as you can see, 'remotefunc' is defined in the global 'window' scope, so the window object is supposed to be able to 'see' it.
I thought the problem was probably the closure stuff in the 'controller' definition, so I did a direct $.getScirpt without using the 'controller':
$.getScript( 'http://path/to/remote.js', function(){
window['remotefunc'].apply( this, null ); // this worked
} );
strange. So it is about the 'controller' implementation(I kind need it)! Anybody can help me out with this? How to fix the 'controller' implementation so the
window[js].apply(this,null);
can actually work?
Thanx.
The reason it's telling you window['remotefunc'] is undefined is because you are not giving it time to actually download and execute the remote script before attempting to call a function defined in it.
The remote script is loaded asynchronously, which means the script execution isn't paused while waiting for a response.
You will need to either re-implement the getScript method to be synchronous or somehow work your class around the fact that the function will not be available in any determinate amount of time.
EDIT: Just found another possible solution, try calling this before your request
$.ajaxSetup({async: false});
This will make the getScript method synchronous
When using something like getSript, it's important to remember that it is fetching asynchronously. Meaning, the browser fires off the request and while that's happening, code after that line executes without pause.
jQuery provides a callback function parameter to get script that allows you to do something after the asynchronous fetch is finished.
Try this:
var script = function(){
var scripts = {};
return {
load: function(jsurl, callback){
$.getScript(jsurl, callback);
},
run: function(js){
window[js].apply(this,null);
}
}
};
Then, when using it:
ctlr.load( 'remote.js', function(){
// remote script successfully loaded
ctlr.run( 'remotefunc' );
});
Could this be a timing issue?
In your working example you call the function in a callback which jQuery will not invoke until the script is loaded. In your non-working example, you call the function immediately after getScript which is asynchronously loading the script.