Loading external scripts async in JavaScript - javascript

This is my first module i'm writing in JS and I want to make sure i'm doing it right.
It is going to be a simple gallery module but I want it to be able to display youtube and vimeo movies also.
In my module I have function called init(). Before i'm displaying the gallery first I want to add all necessary libs, like youtube iframe api and vimeo js api.
only after they are loaded I want to display the gallery.
so my module looks something like this:
myModule = {};
myModule.isYoutubeApiLoaded = false;
myModule.isVimeoApiLoaded = false;
myModule.controller;
myModule.init = function (){
myModule.loadYoutubeapi();
myModule.loadVimeoApi();
myModule.startController();
}
now startController function looks like this:
myModule.startController = function (){
myModule.controller = setInterval(function(
if (myModule.isYoutubeApiLoaded && myModule.isVimeoApiLoaded ) {
myModule.buildGallery();
clearInterval(myModule.controller);
}
), 5);
}
in loadYoutubeapi() and loadVimeoApi() i'm setting the given flags when scripts are loaded.
is it a good approach?

No, it's not a good approach. It will load CPU and will possibly have unnecessary delay of 5 milliseconds.
Better way would be to add callbacks to loadYoutubeapi() and loadVimeoApi(). Once they finish they must call your new function (e.g. moduleHasLoaded()), which will count loaded modules. When all will be loaded you can call startController().
It will save CPU and will not have a unnecessary delay at all.
Something like this:
myModule = {};
myModule.isYoutubeApiLoaded = false;
myModule.isVimeoApiLoaded = false;
myModule.loaded = false;
myModule.controller;
myModule.loadYoutubeapi = function() {
/* Do your stuff. */
myModule.isYoutubeApiLoaded = true;
myModule.moduleHasLoaded();
}
myModule.loadVimeoApi = function() {
/* Do your stuff. */
myModule.isVimeoApiLoaded = true;
myModule.moduleHasLoaded();
}
myModule.moduleHasLoaded = function() {
if (!loaded && myModule.isYoutubeApiLoaded && myModule.isVimeoApiLoaded ) {
loaded = true;
myModule.buildGallery();
}
}
myModule.init = function (){
myModule.loadYoutubeapi();
myModule.loadVimeoApi();
}

The simple solution is to use a script loader like lazyload. It allows you to specify the scripts you want to load and run a callback when the are actually loaded, i.e.:
LazyLoad.js([
"http://www.youtube.com/apiplayer?enablejsapi=1&version=3",
"...vimeo script...",
"...other scripts..."
],
function() {
myModule.buildGallery();
}
);
The function will be called when all scripts are loaded. The caveat is that you get no error callback, i.e. if a script fails to load. There are also other script loaders besides lazyload.
More complicated solution, but better if you are working on a medium to large size client-side application: Refactor your modules to use require.js.

Related

How to dynamic load Jquery inside a JavaScript Class constructor and proceed the execution only after jquery has been fully loaded

This is actually a merge of three differents questions already all well answered here on stack overflow !
1 - How to Dynamic Load a JavaScript file from inside a Js Script :
2 - How to Dynamic Load Jquery
3 - SetTimeout inside a JS Class using this
Basically, I am building a class that will inject some pages inside my clients's website.
To do so, the client just need to add my script src on the page.
<script src="<my_pub_http_address>/MyClass.js">
Once the script is invoked, I will need jquery to continue the execution !
But, I cannot know if the website that invoked the scripts has jquery already loaded.
So, I need to check if jquery is loaded, if not, I will have to load it, append to head and only then when jquery is loaded and working, I will proceed with the script's execution .
PS: this is a kind of legacy answer ! I already had the solution beforehand !
So, any improvement will be appreciated !
That's the solution I've found:
// MyClass.js
var counterLoopLoad = 0;
class MyClass {
constructor(){
// do the code that does not need jQuery
return this.Init()
}
JqueryLoader() {
// Loop Breaker
counterLoopLoad ++;
if (counterLoopLoad == 100) {
throw 'I need jQuery in order to do what I am suppose to do!';
}
var __jquery = document.createElement('script');
__jquery.src = "http://code.jquery.com/jquery-latest.min.js";
__jquery.type = 'text/javascript';
// if needed ....
// __jquery.onload = function() {
// some code here
//
// };
// must be prepend !!! append won't work !!!!
document.head.prepend(__jquery);
// here is the point that makes all work !!!!
// without setTimeOut, the script will get in a loop !
var that = this;
setTimeout(function () {
that.Init();
}, 500);
}
Init() {
if (typeof jQuery == 'undefined') {
return this.JqueryLoader();
}
jQuery.ajax(...);
}
}

How to inject a JavaScript function to all web page using Firefox extension

I am developing a Firefox addon. What I want to do is to inject a custom JavaScript function.
i.e.
function foo() {..}
So all the pages can call the foo without define it first.
I have look from other answer such as: http://groups.google.com/group/greasemonkey-users/browse_thread/thread/3d82a2e7322c3fce
But it requires modification on the web page. What if perhaps I want to inject the function foo into Google.com? Is it possible to do so?
I can do it with a userscript, but I want to use the extension approach if possible.
The first thing I thought when reading your question was "this looks like a scam". What are you trying to achieve?
Anyway, here's a Jetpack (Add-on builder) add-on that injects a script in every page loaded:
main.js:
const self = require("self"),
page_mod = require("page-mod");
exports.main = function() {
page_mod.PageMod({
include: "*",
contentScriptWhen: "ready",
contentScriptFile: self.data.url("inject.js")
});
};
inject.js:
unsafeWindow.foo = function() {
alert('hi');
}
unsafeWindow.foo();
What if you make a simple href with javascript function on the page.
Like bookmarklets work.
Here is a sample code :
function(scriptUrl) {
var newScript = document.createElement('script');
// the Math.random() part is for avoiding the cache
newScript.src = scriptUrl + '?dummy=' + Math.random();
// append the new script to the dom
document.body.appendChild(newScript);
// execute your newly available function
window.foo();
}('[url of your online script]')
To use it, put your script's url.
It must be only one line of code, url formated, but for code readability I've formated it.
I've never developed a Firefox extension, but for javascript injection that's how I would roll.
Hope it helped.
You can use Sandbox
// Define DOMContentLoaded event listener in the overlay.js
document.getElementById("appcontent").addEventListener("DOMContentLoaded", function(evt) {
if (!evt.originalTarget instanceof HTMLDocument) {
return;
}
var view = evt.originalTarget.defaultView;
if (!view) {
return;
}
var sandbox = new Components.utils.Sandbox(view);
sandbox.unsafeWindow = view.window.wrappedJSObject;
sandbox.window = view.window;
sandbox.document = sandbox.window.document;
sandbox.__proto__ = sandbox.window;
// Eval your JS in the sandbox
Components.utils.evalInSandbox("function foo() {..}", sandbox);
}, false);

Loading external Javascript Sequentially

I am working on a javascript that sequentially loads a list of other external javascript.
The code I have so far:
function loadJavascript(url){
var js = document.createElement("script");
js.setAttribute("type", "text/javascript");
js.setAttribute("src", url);
if(typeof js!="undefined"){
document.getElementsByTagName("head")[0].appendChild(js)
}
}
loadJavascript("Jquery.js");
loadJavascript("second.js");
loadJavascript("third.js");
The problem I ran into is that sometimes the other js files loads before the Jquery file completes its loading. This gives me some errors.
Is it possible to make it so that the next JS file is only initiated when the previous file is finished loading.
Thanks in advance
Sure there is, but there's entire libraries written around doing this. Stop reinventing the wheel and use something that already works. Try out yepnope.js or if you're using Modernizr it's already available as Modernizr.load
loadJavascript("Jquery.js");
$(function(){
$.getScript('second.js', function(data, textStatus){
$.getScript('third.js', function(data, textStatus){
console.log("loaded");
});
});
}
Also, consider using the Google or Microsoft CDN for the jQuery, it will save you bandwidth and hopefully your visitors will already have it cached.
Actually, it's not necessary to load jquery within a js function. But if you insist, you can callback to make sure other js loaded after jquery.
Still, I recommend you load jquery just before </body> then use $.getScript to load other .js
You could do a check to see if jQuery is loaded, not the best way to do it, but if you really have to wait until jQuery is loaded before loading the other scripts, this is how I would do it, by checking for $ :
loadJavascript("Jquery.js");
T=0;
CheckIfLoaded();
function CheckIfLoaded() {
if (typeof $ == 'undefined') {
if (T <= 3000) {
alert("jQuery not loaded within 3 sec");
} else {
T=T+200;
setTimeout(CheckIfLoaded, 200);
} else {
loadJavascript("second.js");
loadJavascript("third.js");
}
}
In technical terms: Browsers have a funny way of deciding I which order to execute/eval dynamically loaded JS, so after suffering the same pain and checking a lot of posts, libraries, plugins, etc. I came up with this solution, self contained, small, no jquery needed, IE friendly, etc. The code is extensively commented:
lazyLoader = {
load: function (scripts) {
// The queue for the scripts to be loaded
lazyLoader.queue = scripts;
lazyLoader.pendingScripts = [];
// There will always be a script in the document, at least this very same script...
// ...this script will be used to identify available properties, thus assess correct way to proceed
var firstScript = document.scripts[0];
// We will loop thru the scripts on the queue
for (i = 0; i < lazyLoader.queue.length; ++i) {
// Evaluates if the async property is used by the browser
if ('async' in firstScript ) {
// Since src has to be defined after onreadystate change for IE, we organize all "element" steps together...
var element = document.createElement("script");
element.type = "text/javascript"
//... two more line of code than necessary but we add order and clarity
// Define async as false, thus the scripts order will be respected
element.async = false;
element.src = lazyLoader.queue[i];
document.head.appendChild(element);
}
// Somebody who hates developers invented IE, so we deal with it as follows:
// ... In IE<11 script objects (and other objects) have a property called readyState...
// ... check the script object has said property (readyState) ...
// ... if true, Bingo! We have and IE!
else if (firstScript.readyState) {
// How it works: IE will load the script even if not injected to the DOM...
// ... we create an event listener, we then inject the scripts in sequential order
// Create an script element
var element = document.createElement("script");
element.type = "text/javascript"
// Add the scripts from the queue to the pending list in order
lazyLoader.pendingScripts.push(element)
// Set an event listener for the script element
element.onreadystatechange = function() {
var pending;
// When the next script on the pending list has loaded proceed
if (lazyLoader.pendingScripts[0].readyState == "loaded" || lazyLoader.pendingScripts[0].readyState == "complete" ) {
// Remove the script we just loaded from the pending list
pending = lazyLoader.pendingScripts.shift()
// Clear the listener
element.onreadystatechange = null;
// Inject the script to the DOM, we don't use appendChild as it might break on IE
firstScript.parentNode.insertBefore(pending, firstScript);
}
}
// Once we have set the listener we set the script object's src
element.src = lazyLoader.queue[i];
}
}
}
}
Of course you can also use the minified version:
smallLoader={load:function(d){smallLoader.b=d;smallLoader.a=[];var b=document.scripts[0];for(i=0;i<smallLoader.b.length;++i)if("async"in b){var a=document.createElement("script");a.type="text/javascript";a.async=!1;a.src=smallLoader.b[i];document.head.appendChild(a)}else b.readyState&&(a=document.createElement("script"),a.type="text/javascript",smallLoader.a.push(a),a.onreadystatechange=function(){var c;if("loaded"==smallLoader.a[0].readyState||"complete"==smallLoader.a[0].readyState)c=smallLoader.a.shift(),
a.onreadystatechange=null,b.parentNode.insertBefore(c,b)},a.src=smallLoader.b[i])}};

How to implement `using` in javascript?

I would like to make something like this (similar to c#):
using("content/jquery.js");
$("div").fadeOut();
So if content/jquery.js is not on a script in the head I would like to load it and continue the execution after it loads.
Is it possible to implement this or something similar? Note: I could have more than 1 using
These are called script loaders.
You can take a look at RequireJS, which behaves in a very similar way:
require(["helper/util"], function() {
//This function is called when scripts/helper/util.js is loaded.
});
There's also LabJS and ControlJS, which are more focused on async script loading rather than dependencies, but may be worth checking out. Also in this category, our very own #jAndy's SupplyJS.
You can use getScript:
$.getScript('test.js', function() {
alert('Load was performed.');
});
If you know the name of the functions you'll be calling ahead of time you can use typeof to check for their presence:
if (typeof($) == 'function') {
// Code here that uses jQuery
} else {
// Something else here that loads the script.
}
If you're not certain what functions you'll be using you probably want to retrieve a list of the script elements and check them. Something more along these lines:
var scripts = document.getElementsByTagName('script');
for (var i = 0, l = scripts.length; i < l; ++i) {
if (scripts[i].src == myVal) {
// do something here
} else {
// wait for the script to load, or just leave off the else.
}
}
Are the two approaches I can think of off the top of my head.

Wait for iframe to load in JavaScript

I am opening an iframe in JavaScript:
righttop.location = "timesheet_notes.php";
and then want to pass information to it:
righttop.document.notesform.ID_client.value = Client;
Obviously though, that line isn't going to work until the page has fully loaded in the iframe, and that form element is there to be written to.
So, what is the best/most efficient way to address this? Some sort of timeout loop? Ideally I would really like to keep it all contained within this particular script, rather than having to add any extra stuff to the page that is being opened.
First of all, I believe you are supposed to affect the src property of iframes, not location. Second of all, hook the iframe's load event to perform your changes:
var myIframe = document.getElementById('righttop');
myIframe.addEventListener("load", function() {
this.contentWindow.document.notesform.ID_client.value = Client;
});
myIframe.src = 'timesheet_notes.php';
Again, this is all presuming you mean iframe, not framesets.
I guess you can pretty easily do this with jQuery... jQuery Home
Just hook the page to the jQuery $ function ()... e.g.
$(document).ready(function() {
$('iframe').load(function() {
// write your code here....
});
});
Have a quick look at the file uploader example here:
Using iframes for multiple file uploads...
iFrame could have dynamically loaded elements and the best option to work with them is to use recursion:
$('iframe').ready(function() {
var triggerMeAgainIfNeeded = function() {
setTimeout(function() {
var neededElm = $('.someElementThatLoadedAfterIframe');
if (neededElm.length > 0) {
// do your job
} else {
triggerMeAgainIfNeeded();
}
}, 10);
}
});
try this one...
$('iframe').each(function() {
$(this).ready(function() {
$('#result').html("ready");
});
});

Categories