Execute local Javascript after remote javascript loads? - javascript

I have a page where I am using jquery/ajax to pull down a chunk of HTML/JS from another component and injecting it into the page. That HTML references additional JS files, and I need those referenced JS files to be loaded before I run my javascript.
The HTML/JS that is being injected looks something like this:
<script type="text/javascript" src="http://myserver/js/ABunchOfStuff.js"></script>
<div>
blah blah blah
</div>
<script type="text/javascript">
//"sourceList" is defined in the ABunchOfStuff.js above, but it's not available by the time this executes.
$("input#autocomplete").autocomplete({
source: sourceList,
minLength: 2
});
</script>
Normally I would just hook into a window load event or a $(document).ready() or whatever, but in this case the window and document have already been completely loaded, and now we're adding additional content after the fact.
One possiblity would be to put a recursive setTimeout call in that would keep firing until the referneced javascript was available, but that's pretty ugly.
So is there any clean way to trap the event of a referenced javascript has been loaded and to execute code at that time?
Thanks

You can also use getScript and do your autoComplete in the success callback:
jQuery.getScript( 'http://myserver/js/ABunchOfStuff.js', function(data, textStatus) {
$("input#autocomplete").autocomplete({
source: sourceList,
minLength: 2
});
} );

The big question is, how do you inject this script ?
If you using "standard" script tag insertion, you can go and watch for the onload event (onreadystatechange in IE).
var scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = 'somewhere/somename.js';
scr.onload = scr.onreadystatechange = function() {
if( /complete|loaded/.test(scr.readyState) ) {
// do something
}
else {
// do something
}
};

What you are doing wrong here is not waiting for the DOM to load.
If you change your .autocomplete to only execute once the DOM is loaded through $(document).ready it will have executed the ABunchOfStuff.js
Like this:
(function($) {
$(document).ready(function() {
$("input#autocomplete").autocomplete({
source: sourceList,
minLength: 2
});
}
}(jQuery));

If you control the http://myserver/js/ABunchOfStuff.js file, then you can call your other JS from it when it first executes. Since it executes when it first loads and when it's available, you have the perfect timing.
If this JS file is used other places too, you could add some generic functionality to it for calling a callback when it executes by adding something like this to it:
try {
if (aBunchOfStuffCallbacks) {
for (var i = 0; i < aBunchOfStuffCallbacks.length; i++) {
aBunchOfStuffCallbacks[i].call(this); // call callback to announce we're loaded
}
} catch(e) {}
And, then in any web page where you want to be called when aBunchOfStuffCallbacks was loaded, you would just do this:
var aBunchOfStuffCallbacks = [];
aBunchOfStuffCallbacks.push(myFunc);
function myFunc() {
// put my code here for when aBunchOfStuffCallbacks is loaded
}
This would allow for multiple callbacks. The simpler version for just one callback looks like this:
try {
if (aBunchOfStuffCallback) {
aBunchOfStuffCallback.call(this); // call callback to announce we're loaded
}
} catch(e) {}
And, it would look like this to set it:
var aBunchOfStuffCallbacks = function () {
// put my code here for when aBunchOfStuffCallbacks is loaded
}

Related

Which one happens first? WebComponentsReady or dom-change?

I just started on Polymer. There seems to be two events indicating content is ready:
// Listen for template bound event to know when bindings
// have resolved and content has been stamped to the page
app.addEventListener('dom-change', function() {
console.log('Our app is ready to rock!');
});
// See https://github.com/Polymer/polymer/issues/1381
window.addEventListener('WebComponentsReady', function() {
// imports are loaded and elements have been registered
});
I wonder if it is necessary to wrap them together and put the code inside, to make sure that the document is fully loaded before doing any script, for example:
app.addEventListener('dom-change', function() {
window.addEventListener('WebComponentsReady', function() {
// scripts go here
});
});
However, I don't know what is the correct way to do so in all browsers. If WebComponentsReady happens before dom-change, the inside script never execute.
Heck, this might not even be necessary because the polymer-starter-kit doesn't wrap them together. In that case, which types of script should go inside dom-change event and which types of script should go inside WebComponentsReady event?
Use the native ready callback as described here.
<script>
(function() {
Polymer({
is: 'example-element',
properties: {...},
ready: function() {
// access a local DOM element by ID using this.$
this.$.header.textContent = 'Hello!';
}
});
})();
</script>

Correct & simplest way of calling one js file inside another js

I want to call a third party js in the body tag of my code after I define a particular object. Whats the best way to add the third party js inside my js file? I read that below is one way to do it but it is harmful.
var myObject = { /*this is my object */};
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "thirdparty.js";
document.body.appendChild(js);
I'm a js newbie. I researched a bit & saw options such as reuire js etc but I'm not very clear. Can any one suggest me the correct & simplest way to include third party js in my js file? way to do it? I want this js to be called only after I define myObject in the body.
\
Like this: #veritas
myObject = { //my object };
add_html += ' < div id="xyz" >';
add_html += '< /div >';
$some_div.find('li').eq(2).after( add_html );
$('#xyz').append(myObject);
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "thirdparty.js";
document.body.appendChild(js);
If you want to insert the 3rd party library with JS, you can add an event listener to make sure it's loaded and then run your code.
var myObject = { /*this is my object */},
js = document.createElement("script");
js.type = "text/javascript";
js.onload = function () {
myObject.callMethod();
}
document.body.appendChild(js);
js.src = "thirdparty.js";
The most important part is to put the js.src = setting after attaching your listener because the resource may trigger load before your manage to add a event listener to it.
EDIT
I'm not clear what does this part do js.onload = function () { myObject.callMethod(); } .. What is callMethod()? .
.onload part
Every resource element like script will trigger an event load after successfuly loading the content inside your document. The .onload part is adding an event listener to that particular event (NOTE: this code can be also written differently eg. js.addEventListener("load", callback)).
Why do we want to trigger the code after load? Simple, because the browser is doing the fetch and execute asynchronously and the objects or methods you expect to get from the thirdparty.js may not be ready before next operation in your script (so you would get undefined values).
.callMethod() part
This is just my dummy code, I was assuming you want to run a method from myObject that uses some thirdlibrary objects and methods`.
Why all the fuss ? Go simple
<script>
var myObject = { /*this is my object */};
if (myObject.otherLibIsNeeded) {
var newScript = document.createElement('script');
newScript.src = '/path/to/thirdparty.js';
document.head.append(newScript);
}
</script>
The best way to do this is to just include the third party library in the head of the HTML file where you include your own js, making sure it is before yours:
<script src="thirdparty.js"></script>
<script src="myScript.js"></script>
One more easy way is to do it using ScriptManJS library. It makes sure dependencies are loaded correctly and you do not have to bother about it when using ScriptManJS.
1) Add script tag to the head section of html to include the library:
<script type="text/javascript" src="scriptman.min.js"></script>
2) Somewhere in beginning of code add:
new ScriptMan();
3) Perform inclusions in one of the possible syntaxes. I will provide several options how to do that just a broader picture. I am sure you will choose the one which will best suit your needs:
Option 1:
Just include two files - no additional code is needed when they are loaded.
S.require( [
'file_with_your_object.js',
'third_party.js'
], {
sync: true
} );
// Files may not be loaded yet when executing code here
Option 2:
If object is defined in main file, it becomes simple as this.
var yourObject = { /* Object contents */ };
S.require( 'third_party.js' );
// Files may not be loaded yet when executing code here
If you do not like the fact that file may not be loaded yet below this fragment, modify the last line to this:
S.require( 'third_party.js' ).then( function() {
// Here third_party.js will definitely be loaded
} );
// Here third.party.js may not be ready yet
Option 3
Same as option 1 - different syntax.
S.require( 'file_with_your_object.js' ).then( function() {
S.require( 'third_party.js' );
} );
Option 4:
How I would do it with ScriptManJS.
file_with_your_object.js
return { /* Object contents */ };
main file (i.e. index.html)
S.require( 'file_with_your_object.js' ).then( function( yourObject ) {
S.require( 'third_party.js' ).then( function() {
// Everything is loaded and yourObject contains your object here
} );
} );
Or even like this (in main file):
S.require( [
'file_with_your_object.js',
'third_party.js'
], {
sync: true
} ).then( function( yourObject ) {
// Everything is loaded and yourObject contains your object here
} );
If for some reason third party library would not like to be included like that (low probability), you can add one more option next to sync:
...
{
sync: true,
nakedFiles: [ 'third_party.js' ]
}
...
Hope this helps :)

JavaScript global callback functions are not called?

I'm building a small web app with a few other people. I want to allow the other developers to define a couple of functions that are always called when the document is ready.
Our app inserts the following script into the HTML body of every page:
<script type="text/javascript">
(function(){
window.Utils = {
funcs: {}
};
$(document).ready(function(){
alert('Calling funcs...');
var startFunc = Utils.funcs['start'];
if (startFunc != undefined){
startFunc();
}
var finishFunc = Utils.funcs['finish'];
if (finishFunc != undefined){
finishFunc();
}
});
})();
</script>
Then, in a separate .js file, a developer should be able to do the following:
Utils.funcs['start'] = function(){
alert('Starting...');
};
Utils.funcs['finish'] = function(){
alert('Finishing...');
};
But this doesn't work. The functions are never called?
jsFiddle link: http://jsfiddle.net/XvQtF/
jsFiddle's (very surprising) default is to put your code in a window load handler. (You can see this on the left at the top, the second drop-down box says "onload".) That happens very late in the loading process, long after ready has fired. So the functions aren't added until after you've tried to run them.
If the other developers put their functions in Utils.funcs in script elements after your element defining Utils but without waiting for a load event, it's fine: Updated Fiddle
For what it's worth, though, I would lean toward using a pub/sub solution rather than a single function. If you want to have more than one start function, for instance, your current structure doesn't allow it.
jQuery has Deferred and Promise now, which can be used for this. Here's a simple example of that: Live Copy | Live Source
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<meta charset=utf-8 />
<title>Pub/Sub with Deferred and Promise</title>
</head>
<body>
<script>
(function($) {
// Deferred objects for our app states
var start = new $.Deferred(),
finish = new $.Deferred();
window.Utils = {
start: start.promise(), // Only expose the Promise of each
finish: finish.promise() // " " " " " "
};
$(function() {
alert("Calling funcs...");
// Start the app
start.resolve();
// Finish the app (or whatever)
finish.resolve();
});
})(jQuery);
</script>
<script>
// Module 1
Utils.start.then(function() {
alert("Module 1 Started!");
});
Utils.finish.then(function() {
alert("Module 1 Finished!");
});
</script>
<script>
// Module 2
Utils.start.then(function() {
alert("Module 2 Started!");
});
Utils.finish.then(function() {
alert("Module 2 Finished!");
});
</script>
</body>
</html>
Set up Utils in a separate script to be loaded as the very first one. Also, do this unconditionally (not in any callback etc):
/* load as first script, sets up a global container-object for later use */
var Utils = {
funcs: {}
}
Note that it is ok to define a global variable in the global scope.
/* other script */
(function(){
function myFunc() { /*...*/ };
// now store a reference in Utils
Utils.funcs.start = myFunc;
})();
As mentioned in an other answer: Be aware of the loading and calling order of your various scripts / code:
$(document).ready is essentially the "DOMContentLoaded"-event with most browsers (but earlier versions of IE). "DOMContentLoaded" fires, when all inline-resources originally found in the head-section have been loaded and the DOM-structure of the body is present.
Since this does not include any injected content, it is probably granted, that the event is fired before any modular library (which are loading modules by injecting script tags) is fully loaded and present. (Given that these scripts are loading concurrently with images and other inline-stuff using just a handful of network-slots/sockets provided by the browser, they are probably one of the last things to be ready in your whole loading-process.)
Instead of just using
startFunc() and
finishFunc()
try using this
startFunc.apply(this, null) and
finishFunc.apply(this, null)
this will invoke the functions.
also make sure that
Utils.funcs['start']
Utils.funcs['finish']
is getting initialized before they are called.

Scope issue inside a custom object

I think I am having a scope visibility issue I can't figure out exactly: when I log the variable displayatonce I get back the right result, but as I try to use the buttons I get nothing in return. I have also tried to log this.navbuttons but all I get is an empty set... I really don't get what's wrong with this code.
<!-- html code -->
<div id="nav">
Previous
Next
</div>
/* Js Script with jQuery */
(function() {
var NewsNavigator = {
init: function(config) {
this.navbuttons = config.navbuttons;
this.displayatonce = config.displayatonce;
this.counter = 0;
this.showNews();
this.enableNav();
},
showNews: function() {
console.log(this.displayatonce);
},
enableNav: function() {
console.log(this.navbuttons);
this.navbuttons.on('click', function() {
console.log("clicked");
});
}
};
NewsNavigator.init({
displayatonce: 3,
navbuttons: $('div#nav').find('a')
});
})();
That is happening because as you are using (function())(); which executes the function immediately, maybe it's running the code before the dom is ready
everything is working fine in the below demo
DEMO
Put all your code inside document ready or at least call the initialize method inside doc ready block like
$(function(){
NewsNavigator.init({
displayatonce: 3,
navbuttons: $('div#nav').find('a')
});
});
Read more about Javascript self executing Anonymous function here
Javascript self executing function "is not a function"
or
http://markdalgleish.com/2011/03/self-executing-anonymous-functions/
You're using jQuery too soon, specifically before the DOM is ready to be searched.
Here is fiddle demonstrating this: http://jsfiddle.net/w7KaY/ (JavaScript is placed in <head>, so init() is invoked pretty early) while here (http://jsfiddle.net/w7KaY/1/), the call to init() is encapsulated in an event handler for jQuery's DOM-ready event.
Make sure the html elements are there in the DOM. I don't see any issue with the script other than the fact you have to use the bind method for binding to events.
this.navbuttons.bind('click', function() {
console.log("clicked");
});

loading javascript file after jquery.ready

I want to load a javascript file at the end of jquery.ready so that the code in my ready handler doesn't have to wait to execute until this large javascript file is loaded.
My jquery.ready code doesn't rely on this javascript file at all.
Would this be a good way to do that?
$(function(){
...
...
$('head').append('<script type="text/javascript" src="/largejs.js"></script>');
});
Use .getScript:
http://api.jquery.com/jQuery.getScript/
$(document).ready(function(){
...
...
$.getScript("largejs.js");
});
solution will check jquery already loaded, if not it will check after some time here 500ms and loop until it found jquery
function loadScriptAfterJQueryReady(jsFile) {
setTimeout(function () {
var loadScript=true;
if (typeof jQuery == 'undefined') {
if (typeof window.jQuery == 'undefined') {
/* jQuery is not loaded */
loadScript=false;
loadScriptAfterJQueryReady(jsFile);
}else{
/* jQuery is loaded */
}
} else {
/* jQuery is loaded */
}
if(true==loadScript) jQuery.getScript(jsFile);
}, 500);
}
loadScriptAfterJQueryReady("my.js");
The quoted "</script>" tag will actually end your block of JavaScript prematurely.
I'd use this method:
var newScript = $(document.createElement('script'));
newScript.src="/largejs.js"
If your application is new and not too far along yet, you could get a lot out of using LABjs for that purpose. It allows all your script files to load in parallel or, even, at any other time that you prefer (on demand).
http://labjs.com/

Categories