In a ASP.NET Masterpage I am using YepNope to unconditionally and asynchronously load jQuery (from the Google CDN, with local fallback) and some scripts which are used on all pages in the site. In the MasterPage I have created a ContentPlaceHolder before the closing body tag (and below the YepNope script that loads those used on all pages) which is used for scripts used on individual page. As jQuery should be available on every page in the site it should not be loaded individually on those pages where there are specific scripts that use it.
The problem I have is that I can't use the callback or complete functions in the yepnope script where jQuery is loaded, as this is on the MasterPage and these are individual page scripts which are only used or added on that page, yet I need to be able to delay the execution of the individual page scripts until yepnope (which appears above the page scripts) has finished loading any dependencies (such as jQuery) used in them.
I can think of two options-
1- Make the script used on the page an external file and load that using the syntax -
yepnope('/url/to/your/script.js');
or
yepnope({ load: '/url/to/your/script.js' });
I'm not sure I like this idea as it introduces an extra HTTP request for a few lines of javascript which isn't going to be used on any other page.
2- Load jQuery again in another yepnope test object block, with the complete function wrapping up the page scripts (calling complete without a test seems to execute the function immediately, before the previous scripts are loaded) and relying on the following-
I am requesting a file twice and it's only loading once? By popular
demand, in yepnope 1.5+ we added the feature that scripts that have
already been requested not be re-executed when they are requested a
second time. This can be helpful when you are dealing with less
complex serverside templating system and all you really care about is
that all of your dependencies are available.
In the page I could presumably load the same version of jQuery from the Google CDN, which based on the above would not actually be loaded twice, and then load the page scripts in an anonymous function called from the complete function of the yepnope test object.
On the plus side this would mean that the page is no longer dependent on jQuery being loaded from the MasterPage, but a negative would be that (even assuming YepNope does not load the script twice now) we would be loading multiple versions of jQuery should the version in the MasterPage be changed without the same happening in the page in the future. From a maintenance point of view I don't feel this is a good idea, especially on the assumption (which I feel you should always make) that another developer would be the one making the changes.
It also does not seem especially elegant.
On balance I will almost certainly use the first option but I would like to know if there is a way to delay or defer scripts on a page until asynchronous loading is completed, and this cannot be done as part of the YepNope test object loading the resources.
How do other developers approach this problem?
I have come up with this as a solution I rather like.
In the MasterPage YepNope test object add the code-
complete: function() {
if (window.pageFunctions !== null && typeof (window.pageFunctions) === "function") {
window.pageFunctions();
}
}
If I want to add any JavaScript code or functions that rely on the dependencies loaded in the MasterPage I just wrap them in a function named "pageFunctions" like so-
<script type="text/javascript">
function pageFunctions() {
$(document).ready(function () {
...
});
}
</script>
I'm still interested in other (possibly better) solutions so I'm going to leave the question open for a couple of days.
I'd also appreciate comments on this as a solution.
Related
I see some strange behavior when I check network tab on chrome tools. jQuery is initiating call to other js files of page. These other files are already loaded but jQuery is adding ?_=some_random_number at the end and calling those files again.
jQuery does not load other javascript-files by default. You must have another library that loads these resources. And by the way, the ?_={timestamp} is used to invalidate browser-caching (the url changes every second, so every second a real request is made, not just a browser-cache lookup).
You can try to enforce the ajax-caching mechanism with:
$.cache = true;
while initializing your code. Thus, the requests will be cached by your browser. But, be aware, that this might tamper with other parts of your code. A better way would be to identify the reason why these extra resources are loaded, and avoid loading.
I need to load some Javascript dynamically after the page has loaded.
Something like this:
page loads
page adds script element with src = "file1.js"
page adds script element with src = "file2.js"
file2.js has a dependency on file1.js - it adds properties to an object defined in file1.js
The problem is that file2.js is loading first (because it is smaller), and is immediately throwing an error because its dependency doesn't exist.
Is there a way for me to defer evaluation/execution of these new scripts until they have all loaded. (There is actually more than two scripts)
If I were to just embed these scripts in a page normally in authored HTML, then it seems that the browser loads all scripts, then evaluates them. But it is behaving differently because I'm adding script elements on the fly.
Thanks
There's a library called RequireJS that handles exactly this situation, and handles every situation you never realized were problems - http://requirejs.org/docs/start.html
Can't you wrap the contents of the files in functions and call them after everything has loaded?
Two suggestions for you:
Have a look at http://requirejs.org/ It solves this problem, among
others.
Or, roll your own simple js loader function. It would be a function that
uses ajax to load a script and then calls a callback when it's done.
Call this loader function in a nested way so that you load your
scripts in the right order.
When helping someone with a website that is rather large and have many includes I hit a bug and couldn't understand it. It was something like this
<script src=...></script>
<div ...
<script>
alert('myscript');
</script>
This page worked fine until I use that section of html and used jquery to ajax the content in. It broke the code but I don't understand why. I seen my alert. I can see the script is being access via the browser network log. I see some of the results of the first script tag but its broken.
Why does order matter? I thought as long as the dom is ready and the script is executed everything should be fine. But in this case being ajaxed in breaks it. I couldn't spend much time but it was curious to see something was dependent on timing.
--edit--
Also I notice if the script is already included on the page ajaxing in the content it will run fine
Does anyone have any idea why ajaxing in content breaks? When its not ajaxed in its fine.
Based on what you say, I give the following assessment :
40% likely -- it is about script load. Dependencies within the ajaxed script to other scripts, variables you define on the page, or even DOM content that is supposedly loaded could be not loaded at the time the script is ajaxed and executed.
Try changing the order of the script tag on the page, putting the loading of the script inside a document ready event handler, or delaying the script execution with setTimeout or defer="defer" -- or if you are really cool create a boot loader that ensures every script is loaded and executed in the exact order you specify : by chaining sets of dependency free simultaneous loads, to sequences of dependent loads.
Script1 <---- depends on --- (Script 2.1, Script 2.2, Script 2.3 )
<--- depends on --- Script3.
So load 1 first, then all the 2. scripts, then 3.
40% likely -- it is about security model. The website where you are ajaxing it from, Where is that? What is its relation to the domain the page is on? Are you testing this on localhost ?If so there are more restrictions. What about on an actual server? Is the Access-Control-Allow-Origin header set appropriately on the ajax response?
20% likely -- it is a circular dependency between the script and the DOM. Say some event handler on element X closes on a scope that references element X. Then there will be a reference to X inside a reference to X so they can't both be garbage collected, so X will persist, cause a memory leak, and possibly create an unusable reference which could be breaking your code.
--edit--
Based on your comment about .html(...) I think .html or .load to run scripts is too messy, and may not even work at all. See .load() doesn't load scripts for a nice way to load scripts with ajax. Or you could jQuery.getScript(...).
Also I seem to remember having issues even with loading html nodes from plain HTML using ajax. It just seems too messy to me. If you want to transfer structured information across ajax, use JSON, then present that information on your side with javascript and HTML. So, don't grab the whole data + presentation, just grab the data, then do your own presentation on your side. It's much neater.
I have the following:
Modernizr.load([
{
load : '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js',
complete : function () {
if ( !window.jQuery ){
Modernizr.load('/js/jquery-1.6.2.min.js');
}
}
},
{
load : ["/js/someplugin.js", "/js/anotherplugin.js"],
complete : function()
{
// do some stuff
}
},
{
load: ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js'
}
]};
I read that Modernizr loads scripts Asyncronously. But in the above example, which ones are loaded async?
Do all of the following get loaded asyncronously?
jquery.min.js
someplugin.js
anotherplugin.js
ga.js
Or is it a combination of async and ordered loading like this:
jquery.min.js is loaded first
Then someplugin.js and anotherplugin.js is loaded async
finally, ga.js is loaded
I'm having a hard time testing which case it is.
You've selected a fairly complex example to dissect. So lets do it in steps.
The three parameter sets {...},{...},{...} will execute sequentially.
Inside the first parameter set you will load jQuery from google's CDN, then when complete you test if jQuery actually loaded. If not (perhaps you are developing offline and the google CDN is not reachable), you load a local copy of jQuery. So these one is "sequential", but really only one of them will load.
In the second parameter set you load someplugin.js and 'anotherplugin.js` simultaneously and asynchronously. So they will be downloaded in parallel. Its great when you can parallel 2 items at a time as that is the "weakest link" for browsers today (yup only IE, Every other browser will parallel 6-8 files at a time).
In the third parameter set you load the google analytics script.
Remember modernizr is a collection of tools. The included loader is actually just a repackaged yepnope. So you can google for more about yepnope.
The idea with the sequential loads is to be able to ensure that dependencies load in order (example your jQuery plugins must load after jQuery framework). The purpose of the parallel downloads syntax in parameter set two is to speed up performance for multiple files that are not co-dependent (example you could load multiple jQuery plugins in parallel once jQuery is loaded as long as they don't depend on eachother).
So to answer your question, your hypothesis #2 is correct. If you'd like to explore more using firebug, add some console.log statements in each parameter set's complete function. You should see the 3 groups complete sequentially every time. Now switch firebug onto the "Net" tab to watch the file requests go out. The files someplugin.js and 'anotherplugin.js` won't necessarily load in the same order everytime. The requests will go out in order, but there timing bars should overlap (showing them as parallel). Testing this locally will be hard because it'll be so fast. Put your two test files on a slow server somewhere or bias them the opposite of what you are expecting (make the first file 1mb and the second <1kb) so you can see the overlapping downloads in the network monitor tab of firebug (this is just an artificial scenario for testing purposes, you could fill a file with repeated JS comments just to make an artificially slow file for testing).
EDIT: To clarify a little more accurately, I'd like to add a quote from the yepnopejs.com homepage. yepnopejs is the resource loader that is included (and aliased) inside modernizr:
In short, whatever order you put them in, that's the order that we
execute them in. The 'load' and 'both' sets of files are executed
after your 'yep' or 'nope' sets, but the order that you specificy
within those sets is also preserved. This doesn't mean that the files
always load in this order, but we guarantee that they execute in this
order. Since this is an asynchronous loader, we load everything all at
the same time, and we just delay running it (or injecting it) until
the time is just right.
So yes, you could put jquery, followed by some plugins in the same Modernizr.load statement and they will be downloaded in parallel and injected into the DOM in the same order as specified in the arguments. The only thing you are giving up here is the ability to test if jQuery loaded successfully and perhaps grab a backup non-CDN version of jQuery if necessary. So it's your choice how critical fallback support is for you. (I don't have any sources, but I seem to recall that the google CDN has only gone down once in its entire lifetime)
I would like to synchronously include a JavaScript file from a different domain via code. This means that using a synchronous XMLHttpRequest will not work. I also want to avoid document.write because my code will be executed when the document is fully loaded. Is this even possible? Does any of the existing JavaScript libraries support that feature?
Basically I want this to work:
<script type="text/javascript">
$(document).ready(function() {
load("path_to_jQuery_UI_from_another_domain");
console.log(jQuery.ui.version); //outputs the version of jQuery UI
});
</script>
EDIT:
My idea is to create a jQuery plugin which loads its JavaScript files based on the enabled features. jQuery plugins can be initialized at any time which means no document.write. It is perfectly fine to load the JavaScript files asynchronously but people expect their plugins to be fully initialized after calling $("selector").something();. Hence the need of synchronous JavaScript loading without document.write. I guess I just want too much.
The only way to synchonously load files is to document.write a script tag into your page. This is generally considered a bad practice. There is probably a better way to do what you actually want, but, in the spirit of transparency:
document.write('<script src="http://otherdomain.com/script.js"></'+'script>')
should do the trick. You have to escape the closing script tag so the parser doesn't close the script tag that you wrote.
**Note that you can't dynamically load scripts that contain a document.write
You should be able to use .getScript()
Edit: Cross-domain requests are always loaded asynchronously in jQuery.
A great library called YepNope exists for loading javascript dependencies from any location - developed by a member of the yayQuery podcast. It can be found here: http://yepnopejs.com/
It's not possible to synchronously execute a script at a URL. Note further that synchronous anything, when networks (or even file systems!) are involved is a Bad Idea. Someone, sometime, somewhere will be on a slow system, or a slow network, or both, and suddenly you've just hung their UI in the process.
Synchronous is bad. Asynchronous with callbacks is good.
Note that, as a worst-case hack, you could overwrite $ with your own function, which returned an object with just the right properties, and you could semi-lazily evaluate all actual calls. This of course breaks if you start relying on immediate execution of the calls, or on their execution being intermingled with the evaluation of arguments, but in the worst case it's not completely implausible.
LABjs.js, is nice library. I used it works well.
http://labjs.com/