To speed up things, my web application loads JavaScript files for certain features on-demand using jQuery's ajax call. I've encountered a race condition problem, though. For example, file B.js depends on file A.js. I've configured a list like list=[A.js,B.js] and I iterate over this list and asynchronously get the scripts. How can I prevent this race condition?
The code that loads the JavaScript files is something like:
loadJS: function(url) {
$.ajax({url: url, dataType: 'script', success: function(){}});
}
This kind of dependency management is not incredibly hard. Without getting too much into how to properly abstract it, if you have an array of files to load, on your ajax callback (which I assume is downloading the contents of the js file to a string) save the string in another array until you download the contents of all the files. Once all the files are in memory, add them to the dom in order.
You will want to add some extra stuff like handling failures and the like but that is a general outline of how to do it.
Suppose you asynchronously load files 1 and 2 using:
function load(URI) {
$.ajax({
url: URI,
dataType: "script",
});
}
and file 2 has dependency on file 1 then the files can be loaded asynchronously as:
load(url1)
.then(load(url2));
and if file 2 doesn't have dependency on file 1 then they can be loaded as:
$.when(load(url1), load(url2))
.then(function(e) { // both scripts loaded successfully});
You can just add script tags instead of making ajax calls:
var scripts = ['a.js', 'b.js'],
idx;
for(idx in scripts) {
if(scripts.hasOwnProperty(idx)) {
document.write(['<script src="', scripts[idx], '"></script>'].join(''));
}
}
Scripts will be loaded asynchronously, but executed synchronously in the order you added them.
Related
I am loading external content into a div element using jquery.load() without a selector. If the content loaded has embedded JS, the JS works as expected. HOWEVER, if the content includes a script tag with src=path-to-js-code the js-code is not loaded.
Am I correct in this observation and if so is there a good solution other than embedding the JS in the loaded content?
EDIT :
A few clarifications and observations:
To load the content I am using
$("#DivId").load("path/to/content.php", CallbackFunction(response, status, xhr) {
error checking and post processing code
});
Changing the load code to:
$.get("path/to/content.php", CallbackFunction(response, status, xhr) {
error checking
$("#DivId").html(response);
post processing
});
Does not seem to change the behavior (more on the behavior below)
I have not tried parsing the response to retreive the script src and then using getScript().
Now more on the behavior...
Using Firefox, it seems that the external JS is loaded but only if it has been about 2 min from the last load. I do not see an attempt in Firebug unless the refresh is about 2m after the last load of the external JS. (weird). When I was making JS code changes and hitting refresh, it was not loading my new code and thus the original question.
So i will withdraw my question in light of this clarified behavior (2m caching?).
Thanks.
Both the .load() and .html() jQuery methods utilise the .innerHTML property. This won't execute scripts added with <script> tag. Use a regular AJAX call e.g. .get() then in the callback use .append() to add your HTML string and the scripts will run once it's parsed e.g.
$.get("path/to/content.php", function(response, status, xhr) {
// error checking
$("#DivId").append(response); // Any <script> tags in the response string will execute
// post processing
});
Thing is you need to make sure you're running trusted code if it's added by .append()
I was wondering you can get the script src in the response text of $.load method with regular expressions, then use $.getScript() method to load the script, maybe something like this:
$("#DivId").load("path/to/content.php", function(response, status, xhr) {
var regexp = new RegExp('script.*?src="(.*?)"'),
execresults = regexp.exec(response);
if(execresults.length > 1)
{
// the first result is the entire match including
// the 'script..src=', so abandon it
var matches = execresults.slice(1);
$.each(matches, function(){
$.getScript(this, function(){
// do something after load script
});
});
}
});
Hope this can help
This is the easy way to load an external JS to your jQuery
$.ajax({
type: "GET",
url: "path/to/content.php",
dataType: "script"
success:CallbackFunction(response, status, xhr)
});
In my main.js file, I have a PageMod that includes a base.js file:
pageMod.PageMod({
contentScriptFile: ["./base.js"]
I have a function inside of my base.js file
function setupPayment(){ /* DO STUFF HERE */ }
Inside my base.js file, I'm also loading other JS files
$.getScript("https://checkout.stripe.com/checkout.js", function(){
$.getScript( self.options.stripe );
});
Inside of my stripe.js file, I'm trying to call the setupPayment function that's in my base.js file
var yearhandler = StripeCheckout.configure({
key: "pk_live_...",
image: "image.png",
name: "SHINE",
description: "Subscription",
panelLabel: "Subscribe",
allowRememberMe: false,
token: function(token) {
plan = "yearly";
setupPayment(token,plan);
}
});
But setupPayment returns undefined.
And after doing some testing, it looks like any script included via $.getScript can't access any functions inside of my base.js content script? Is there any way to make a function inside of my base.js content script global across all my other scripts files that I load?
Thanks for your help!
Edit: the reason setupPayment() has to be in the base.js file, is so that it can communicate with the main.js file and store some information.
You can now export a function from the content script into the page, see this blog post for the gory details. The code would look like this:
function setupPayment(args, callback) {
// some code
callback(result);
// your callback should use postMessage to send data back to the
// content script, see these docs:
// https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/Interacting_with_page_scripts#Communicating_with_page_scripts
}
exportFunction(setupPayment, unsafeWindow, {defineAs: "setupPayment"});
ContentScripts run in sandboxes, I don't know what jquery does internally, but it certainly is not designed to be aware of multiple javascript contexts and mozilla's xray wrappers, so it probably just injects a <script> tag into the DOM which then runs the loaded javascript inside the page context instead of the contentScript sandbox.
And I'm not even sure how you get access to jquery in your sandbox, considering that you only load base.js.
So it looks like a good chunk of your addon code is actually running in/loading scripts into the untrusted page context.
the reason setupPayment() has to be in the base.js file, is so that it can communicate with the main.js file and store some information.
Not quite. You can export a privileged function to the page context via exportFunction, which is available inside the sandbox.
I thought this question would be trivial but I just can't seem to find an answer.
A website (different origin, no control over it) is making available some JSON files. I want some variables of my script to grab the content of those files. I don't care whether it is done synchrnously or not. How would you go ?
using JSONP consist of using your url, with parameters, and add a script file to your page
www.example.com/process?value=1&callback=Func
add the script to your page.
var url = "www.example.com/process?value=1&callback=Func";
var script = document.createElement('script');
script.type= ' text/javascript';
script.src = url;
document.getElementsByTagName("body")[0].appendChild(script);
now you can use the call back function or access the variables that were added from this script.
UPDATE
At the end of your jsonp script you can call your call back function
Ex: php
<?php
if (isset($_GET['callback'])) {
echo $_GET['callback']."();";
// Func(); // will call your function and use your variables.
}
If the remote host does not supply JSONP or CORS, then you will need to place a server-side component on your own domain which fetches the JSON for you and serves it locally.
I am looking for an equivalent to jquery's load() method that will work offline. I know from jquery's documentation that it only works on a server. I have some files from which I need to call the html found inside a particular <div> in those files. I simply want to take the entire site and put it on a computer without an internet connection, and have that portion of the site (the load() portion) function just as if it was connected to the internet. Thanks.
Edit: BTW, it doesn't have to be js; it can be any language that will work.
Edit2:
My sample code (just in case there are syntax errors I am missing; this is for the files in the same directory):
function clickMe() {
var book = document.getElementById("book").value;
var chapter = document.getElementById("chapter").value;
var myFile = "'" + book + chapter + ".html'";
$('#text').load(myFile + '#source')
}
You can't achieve load() over the file protocol, no other ajax request is going to work for html files. I have tried even with the crossDomain and isLocale option on without anything success, even if precising the protocol.
The problem is that even if jQuery is trying the browser will stop the request for security issues (well most browsers as the snippet below works in FF) as it allows you to load locale file so you could get access to a lot of things.
The one thing you could load locally is javascript files, but that probably means changing a lot of the application/website architecture.
Only works in FF
$.ajax({
url: 'test.html',
type: 'GET',
dataType: 'text',
isLocale: true,
success: function(data) {
document.body.innerHTML = data;
}
});
What FF does well is that it detect that the file requesting local files is on the file protocol too when other don't. I am not sure if it has restriction over the type of files you can request.
You can still use the JQuery load function in this context:
You would could add an OfflineContent div on your page:
<div id="OfflineContent">
</div>
And then click a button which calls:
$('#OfflineContent').load('OfflinePage.html #contentToLoad');
Button code:
$("#btnLoadContent").click(function() {
$('#OfflineContent').load('OfflinePage.html #contentToLoad');
});
In the OfflinePage.html you could have to have another section called contentToLoad which would display on the initial page.
So, I know how to use JS or jQuery, etc., to display a "Loading" message while content is loading. I have created a fairly large webapp with a number of JS dependencies and I want to display a "loading" message while all the scripts are loading.
My head tag has a number of <script src=…> tags in it and I want to display a loading message instantly when the user visits the page, and then remove it when all the scripts are loaded.
What's the best way to do this?
Then use $ajax function of jquery to download this javascript files and the add script element in head tag after downloading completes.
like this:
// display loading message here
$ajax("javascriptfile.js",function(file){
// attach downloaded file to head tag now
});
You probably need to lazy loading of the script. The last example from this Lazy Loading show to load .js via YUI. The code from that example is included below for your reference:
var HelloWorld = {
is_loaded: false,
lazyLoad: function(callback) {
var loader = new YAHOO.util.YUILoader();
loader.addModule({
name: "helloworld",
type: "js",
fullpath: "yui_ex/helloworld.js"
});
loader.require("helloworld");
if (callback) {
loader.onSuccess = callback;
}
loader.insert();
},
sayIt: function() {
var args = arguments;
HelloWorld.lazyLoad(function() { HelloWorld.sayIt.apply(HelloWorld, args); });
}
};
Note that you could possibly load the loading image initially and remove it in the callback function. Reading SO Question JQuery to load Javascript file dynamically, you could also use $.getScript() to do the same thing.
You could also find another example in this link