I need to include multiple js files in synchronous manner and once they are successfully included I will call function main() which will create the widget. For that currently I have built a function this way
var scriptArrayHC = [retrieveURL()+"/js/bootstrap.min.js",
"//platform.twitter.com/widgets.js",
retrieveURL()+"/js/html5shiv-2.js",
retrieveURL()+"/js/ie-emulation-modes-warning.js",
retrieveURL()+"/js/ie10-viewport-bug-workaround.js"];
function loadScript(scriptArray, func){
var counter_script=0;
[].forEach.call(scriptArray, function(src){
var script = document.createElement('script');
script.onload = function() {
counter_script++;
//console.log("Script "+counter_script+" --- "+src);
if(counter_script==scriptArray.length) eval(func);
};
script.src = src;
document.getElementsByTagName('head')[0].appendChild(script);
});
}
loadScript(scriptArray, "main()");
It is working fine.. But i think it is not the best way. Does anyone have a better way to do this or is there any library for this?
USE RequireJS to solve the issue
Related
I want to add a element into the existing DOM to have the javascript code run.
I did this with YUI:
var scriptNode = Y.Node.create('<script type="text/javascript" charset="utf-8">alert("Hello world!");<\/script>');
var headNode = Y.one('head');
headNode.append(scriptNode);
It's successfully added to the DOM but it doesn't give me an alert.
Someone knows what the problem is?
I have no idea how YUI's Node.create() function works, so no comment on that. But a simple cross-browser script is:
window.onload = function() {
var s = document.createElement('script');
s.type = 'text/javascript';
var code = 'alert("hello world!");';
try {
s.appendChild(document.createTextNode(code));
document.body.appendChild(s);
} catch (e) {
s.text = code;
document.body.appendChild(s);
}
}
The try..catch block is necessary as most browsers like the first method but some don't and throw an error. The second method covers those. You can also simply eval the code, which is more or less equivalent and what some libraries do.
I found this function in the JQuery source, which seems to do what you want and feels a bit cleaner than the other approaches to me. But then again I am a JS beginner and probably don't see the details. Anyways, somebody might take something useful away from this.
function DOMEval( code, doc ) {
doc = doc || document;
var script = doc.createElement( "script" );
script.text = code;
doc.head.appendChild( script ).parentNode.removeChild( script );
}
Suppose that I want to publish a JavaScript library. There is one problem, though: certain features of this library depend on another library, which is located in a permanent url. Instead of expecting my users to include the dependency, I want to automate this. My question is: what is the most elegant/short way to load such dependency from bare JavaScript?
In other words, I need a short, cross-browser implementation of the "load" function bellow:
MyLib = (function(){
var locals = blabla;
load("http://foo.com/lib.js", function(){
// Library loaded! I can use it here.
...
});
return {...};
})();
Note this can not depend on any other external library (such as requirejs) since such dependency would beat the purpose by definition. After all, it would just force my user to add that dependency instead anyway!
Something like this should do what you want cross-browser
function load(url, callback) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onreadystatechange = callback;
script.onload = callback;
head.appendChild(script);
}
Use Requirejs for that case:
require(["http://foo.com/lib.js"], function (lib) {
});
More info: http://requirejs.org/docs/start.html , https://egghead.io/lessons/requirejs-introduction-to-requirejs
I'm working with an external script (from http://segment.io) and I'm writting an AngularJS module to interact with it.
I am wondering how can I effectively test that their script is well loaded (except than running the real app).
Should I write an end2end test?
Thanks for your help!
// Service is a factory
service.load = function(apiKey) {
// Create an async script element for analytics.js.
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = ('https:' === document.location.protocol ? 'https://' : 'http://') +
'd2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/' + apiKey + '/analytics.js';
// Find the first script element on the page and insert our script next to it.
var firstScript = document.getElementsByTagName('script')[0];
firstScript.parentNode.insertBefore(script, firstScript);
};
if the file leaves behind a global, you can simply look for window.whatever to see if it's loaded.
one very flexible cross-browser pattern i use is what i call a sentinal. You use a wrapper function to wait for the dependencies to arrive before executing the custom code.
for example, if i was dynamically injecting jQuery into the page, and i knew it was needed for something else dynamic:
(function waiter(){
if(!window.jQuery){ return setTimeout(waiter, 37); }
$("#myDiv").fadeOut();
}())
this pattern works independently of any script loader or browser-specific event, and doesn't require the dependancy file to be modified, great for waiting on CDN copies of libraries.
you can easily extent the notion to await several dependencies using modern Array methods:
(function waiter(){
if(![
window.jQuery, // core
window.jQuery.fn.effect, // jq ui
window.jQuery.fn.whizBang // jq ui plugin
].every(Boolean)){ return setTimeout(waiter, 37); }
$("#myDiv").whizBang();
}())
Someone on the AngularJS IRC channel points me to a working solution with Jasmine waitsFor block: github.com/pivotal/jasmine/wiki/Asynchronous-specs
Below a spec following spec:
it('should load the API when called with api key', inject(function ($window, segmentio) {
segmentio.load(apiKey);
waitsFor(function() {
return $window.analytics.initialized == true;
}, "Segmentio never loaded", 10000);
runs(function () {
expect($window.analytics).toBeDefined();
expect($window.analytics.initialized).toBeTruthy();
// Unload
$window.analytics = null;
});
}));
Let's say I have some simple Javascript like:
<script>
var hello = function(){
alert("Hello World!");
}
</script>
.. on a page helloworld.html. If I loaded this script block into another page using Pjax. How do I execute the function hello()?
For security reasons, many browsers will not run Javascript injected by innerHTML, which I'm thinking Pjax likely uses. (Here's a minimal example.)
Maybe the solution proposed in Pjax's issue #48 will help
What worked for me was to place my jQuery code in a function, call it
normally on document.ready (for non-pushState browsers), and then bind
the function to pjax:end, i.e.:
$('body').bind 'pjax:end', mainFunction
This is possible with PJAX. You just need to have the script tag with type text/javascript.
Code from PJAX library:
function executeScriptTags(scripts) {
if (!scripts) return
var existingScripts = $('script[src]')
scripts.each(function() {
var src = this.src
var matchedScripts = existingScripts.filter(function() {
return this.src === src
})
if (matchedScripts.length) {
matchedScripts.remove();
}
console.error("FOUND SCRIPTS", scripts, matchedScripts.length);
var script = document.createElement('script')
script.type = $(this).attr('type')
script.src = $(this).attr('src')
document.head.appendChild(script)
})
}
I'm using a fairly simple system to load javascript dynamically:
include = function (url) {
var e = document.createElement("script");
e.src = url;
e.type="text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
};
Let's say I have a file test.js which has the following contents:
var foo = 4;
Now, in my original script, I want to use
include(test.js);
console.log(foo);
However, I get a 'foo has not been defined' error on this. I'm guessing it has to do with the dynamic script being included as the last child of the <head> tag. How can I get this to work?
It is because you have to wait for the script to load. It is not synchronous like you would think. This may work for you:
include = function (url, fn) {
var e = document.createElement("script");
e.onload = fn;
e.src = url;
e.async=true;
document.getElementsByTagName("head")[0].appendChild(e);
};
include("test.js",function(){
console.log(foo);
});
That is one problem, but it also takes time for the browser to download and parse the remote JS file — and it hasn't done that before you call console.log.
You need to delay things until the script has loaded.
This is easiest done by letting a library do the heavy lifting, jQuery has a getScript method that lets you pass a callback to run when the script has loaded.