I'm currently loading a custom.js file on my site and it calls various functions. However, to keep the size down I only load the libraries needed on certain pages.
Because of this, since the custom.js file is loaded on every page and it calls functions that the particular page may not have, I get undefined is not a function errors on my site on certain pages.
What I would like to be able to do is determine if something is defined before executing the code to keep the errors from popping up.
For an example, I'm using Jarallax (http://www.jarallax.com/) on my front page only with the following:
var jarallax = new Jarallax();
jarallax.addAnimation('div#bigSlider',[{progress:'0%',marginTop:'0px'},{progress:'100%', marginTop:'-200px'}]);
Since Jarallax is only loaded on the homepage and no others I get the undefined function error on all pages but the hompeage. How could I first confirm Jarallax is loaded before attempting to execute the code?
Since referring to undefined variables raises a ReferenceError exception, you could use a try/catch block to handle the exception.
try {
var jarallax = new Jarallax();
}
catch (e) {
// desired behavior for this situation.
}
More on try/catch blocks.
However, to keep the size down I only load the libraries needed on
certain pages. Because of this I get "undefined is not a function"
errors on my site on certain pages.
So this means you're not doing it properly on every page?
You could solve this by using a wrapper object or class:
(function($){
var wrapper = {
init: function(){
var jarallax;
if (typeof Jarallax == 'function'){
jarallax = new Jarallax();
jarallax.addAnimation('div#bigSlider',[{progress:'0%',marginTop:'0px'},{progress:'100%', marginTop:'-200px'}]);
}
}
};
// once the DOM is read
$(function(){
wrapper.init();
});
}(window.jQuery));
By stalling the init function on the DOM ready, you can be certain the script is loaded if you make sure the script tag for Jarallax is added before the wrapper in the HTML. In any other case the init function won't do a thing.
if (typeof jarallax === "undefined") {
var jarallax = {
obj: {},
return {
obj;
};
Related
I have an AEM page with multiple components, these components have a .js file with a function that encloses all the client side logic. We then call that function inside the component's HTML:
<script>
window.bootstrap_component(function() {
init_component_name();
});
</script>
As stated before init_component_name is the name of a function that encompasses all the logic we need:
function init_component_name() {
//DO STUFF
}
The wrapper bootstrap_component fuction is defined in the shared head.html of all our pages as:
<script>
window.bootstrap_component = function (handler) {
if (typeof handler === 'function') {
if (document.readyState === "complete" || document.readyState === "loaded" || document.readyState === "interactive") {
handler();
} else {
document.addEventListener("DOMContentLoaded", function() {
handler();
});
}
}
}
</script>
This works okay and we don't have any actual issues but we recently started using Bugsnag for error monitoring and reporting and we're getting reports for almost every component saying ReferenceError on page so/and/so init_component_name() is not defined.
The reason I think this is happening is because the init_component_name() function is not declared within the script tag and because this, function(init_component_name) has been attached to the window object it is executing fine and you don't see any console errors.
If I am correct would modifying those scripts tags to be like this work?
<script>
window.bootstrap_component(function() {
window.init_component_name();
})
</script>
A colleague of mine wants to add a timeout to the init_component_name functions of like 1ms but it rubs me the wrong way. Is there a more sensible approach?
If I am correct would modifying those scripts tags to be like this work?
window.bootstrap_component(function() {
window.init_component_name();
})
Yes, but you then have the problem that you're writing multiple data to the global namespace, window, which isn't ideal. What if another third-party script decides to override it?
Ideally you'd have a single namespace and put everything on there, and write only that namespace to window.
window.something = {};
something.bootstrap_component = { //...
and
something.init_component_name = () => {
//DO STUFF
}
Or better still, use modules (though that will need some light code refactoring).
Don't do the timeout hack. It's really, really horrible; what if scripts take longer than a second to load for some reason? You're also forcing your UI to wait a second, often unnecessarily. This hack tends to feature where chronology and scope has not been thought out properly.
I am having a weird issue that is rather difficult to recreate, but I will try to explain to the best of my abilities.
I have a setup.js which is a file that includes parameters for the scripts to be added to the document by the main script, like so:
{
script1: true,
script2: true,
script3: false
}
I have a main script file, lets call it main.js which basically checks the setup file and then for every "true" does the following:
var element = document.createElement('script');
element.src = "path/to/file.js"
document.getElementsByTagName('body').appendChild(element);
Now I've checked using console.log, and the main.js file is the one that has to execute all the way to the end until any of the files added to the body begin loading, however they are then loaded one-by-one and should become ready for usage.
Now here comes the tricky part
I need to know when ALL of them are finished loading in order to be able to use their functions in the rest of the main.js file, so I need some sort of "onload" handler. I've tried to attach a addEventListener('load', function(){}) to the last element that was added, and it worked.
However.....
Once I changed the event listener function to something such as:
addEventListener('load' function(){
script1Function(); //Which is defined in script1
script2Function(); //Which is defined in script2
}
It started throwing errors such as "script1Function is not defined".
I know that both scripts are loaded at this point, because I did a console.log("I have loaded") as the last line of each script, and those appeared in the console, but it is not possible to use the function definitions that were defined in these files.
What is even more.... if I instead do the following event handler:
addEventListener('load' function(){
alert("Last script loaded"); //This line was ADDED
script1Function(); //Which is defined in script1
script2Function(); //Which is defined in script2
}
It all suddenly works.
Adding "alert("whatever");" allows me to use the functions defined in the files.... I would assume it has something to do with "time needed for me to click "OK" on the alert dialog... but the entire thing doesn't make sense, because I know functions are defined by the time I am calling them.
/*
Example usage:
importAsync(["script1.js", "script2.js", "script3.js"]);
waitUntil(function() {
return ((typeof(script1Function) !== "undefined")
&& (typeof(script2Function) !== "undefined")
&& (typeof(script3Function) !== "undefined"));
});
*/
function waitUntil(testFunc) {
var start = Date.now();
var timeout = 3000;
do {
if((Date.now() - start) > timeout) {
console.log("waitUntil() timed out");
return false;
}
} while(!testFunc());
}
This is what I meant in the comments.
I direct your attention to the example above the function:
importAsync(["script1.js", "script2.js", "script3.js"]);
waitUntil(function() {
return ((typeof(script1Function) !== "undefined")
&& (typeof(script2Function) !== "undefined")
&& (typeof(script3Function) !== "undefined"));
});
importAsync is whatever function/method you have for loading the .js files - it's not relevant.
What is relevant, is waitUntil and the function you pass it. In the example above, I check whether functions from all three scripts exist (ergo: are not equal to undefined) and only then will execution of the script continue, due to the nature of the do..while loop. As I also mentioned in the comments, you're going to want a timeout on the do..while loop to prevent an infinite loop. You could add a parameter to waitUntil for the timeout as well if you'd like.
I hope this gives you a basic idea of what I meant in the comments and if you have any further questions, fire away!
I am currently working on a data-intensive web application that frequently communicates with an external API and retrieves JSONP data when returned. The script depends upon a library called head.js v1.0.3. http://headjs.com/ to accomplish this. However, I noticed that in IE 11 for some reason, the onload event for the script sometimes, but not always, fires before the script has actually loaded into the browser. The behavior is demonstrable whether using head.js or not. Alternatively, I may create a script element with the onload event set to capture the returned data. Sometimes it works, and sometimes not. Even more weird is that once it happens the first time, it seems to keep happening for the duration of the browser session.
Any ideas for a workaround?
Here is some example code:
//somejson.js
/*
window["queryResult"] = {blah:'blah'}
*/
function loadScript() {
head.load("/somejson.js", afterScriptLoad)
}
function afterScriptLoad() {
var result = queryResult
//Throws error because window.queryResult is sometimes undefined
}
After a little bit of research, it seems the only way around this bug is to modify the API so that once the variable holding the JSONP is initialized, the script itself triggers the callback. Unfortunately, this would not work as a solution for others if they do not have access to modify whatever API is in use, but it does solve the problem for me.
//somejson.js
/*
window["queryResult"] = {blah:'blah'}; scriptCallback()
*/
function loadScript(callback) {
var c = new afterScriptLoad(callback)
window["scriptCallback"] = c
head.load("/somejson.js", c)
}
function afterScriptLoad(callback) {
var retrieved = false
return function () {
if (!retrieved) {
retrieved = true
callback(queryResult)
}
}
}
function myCallback(response) {
//do something
}
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
}
};