Is there a solution out there where I can have JavaScript/jQuery autoload dependent files when needed? For example, consider this scenario:
I have an autoloader script listening for when a particular script needs to be loaded.
A jQuery dialog() plugin is called.
The autoloader is told to listen for when this plugin is called, and loads the jQuery UI.
If more dialogs are called in the future, the required script will not be loaded.
Is this too much effort for simply trying to limit bandwidth? Should I just include all of the core files in one superpackage and be done with it?
Thank you for your time.
Yes you should inclde all of the scripts in one file. Or at least most of them groupped like this: jquery.js, global.js (that's where frequently - on more than one, two pages - used scripts should be) and page_specyfic.js.
Imagine that a dialog() is called and the user has to wait for .js to download and plugins to initialise.
Savings in bandwith (if any) wouldn't be worth harming the users expirience.
There are many examples of on demand script loading out there. For example remy sharp has a code sample on his blog that you could either use as is or turn into a jQuery plugin. Unfortunately it may not work in all browsers.
There is also the jQuery Lazy Plugin Loader which loads jQuery plugins on demand rather than up-front. To use it you would need to set up lazy loading for each piece of jQuery UI you are using as follows (name will be the function name for each piece you use):
$.lazy([{
src: 'jquery-ui-1.8.14.custom.min.js',
name: 'dialog'
}]);
You can also use the techniques in this question about loading jQuery itself on demand. For example you can dynamically create a script tag at the time needed to load jQuery UI.
Finally since you are talking about jQuery UI consider getting it from Google's CDN, which is likely cached in the user's browser anyway.
You can try this new jquery plugin. Works like yeapnope.js but more make sense.
http://plugins.jquery.com/plugin-tags/autoloader
$(document).autoLoader(
{
test: $.ui,
loadScript: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery- ui.min.js",
complete: function(){
console.log($.ui);
}
}
);
I wouldn't worry too much. The files are cached. Once one page in your site loads the jquery UI (or any other include file like CSS), the next time it's needed it will be in the user's browser cache, never to be loaded again for days/weeks
Sounds like you want a script loader.
You can't generally do synchronous loading of scripts across browsers, though, so script loaders are necessarily asynchronous. What you're asking for isn't exactly possible since the script needs to load, call a callback, and then continue. You wouldn't want to call some plugin and not know whether it is executing synchronously or not, that gets you into a world of problems.
I recommend you look at DeferJS, a script loader for jQuery:
https://github.com/BorisMoore/jsdefer
From your comments, part of your wish seems to be to keep your code organized. I would recommend RequireJs. It lets you break your code up into clearly separated modules with explicit dependencies. Then when you want to go to production, there's a build tool that will merge them all back together into the (request/bandwidth saving) 2-3 files you want to serve.
Yeah, I have also thought about implementing something like this. I am not sure if it would be worthwhile or not in the end but there are quite a few libraries to do this for you like ensure
you could try something like this but it would be a pain. basically you are checking the type of error caught and message if dialog (the function you are trying to call doesn't exist) load the function and try calling the method again. Like I said it would be a pain to do this everywhere unless some elegant solution was thought of.
function loadDialog() {
$('#myDialog').dialog({});
}
try {
loadDialog()
} catch(e) {
if (e && e.type && e.type=='not_defined' && e.message == 'dialog is not defined') {
//load jQuery plugins...
loadDialog();
}
}
This is a follow-up post for a comment above:
<link rel="stylesheet" href="../system/stylesheets/universal.css" />
<link rel="stylesheet" href="../system/stylesheets/jquery-ui.min.css" />
<link rel="stylesheet" href="../system/stylesheets/uploadify.css" />
<link rel="stylesheet" href="system/stylesheets/style.css" />
<script src="../system/javascripts/swfobject.js"></script>
<script src="../system/javascripts/jquery.min.js"></script>
<script src="../system/javascripts/jquery-ui.min.js"></script>
<script src="../system/javascripts/global.jquery.js"></script>
<script src="../system/javascripts/analog.jquery.js"></script>
<script src="../system/javascripts/funtip.jquery.js"></script>
<script src="../system/javascripts/uploadify.jquery.js"></script>
<script src="system/javascripts/install.jquery.js"></script>
<link rel="stylesheet" href="system/templates/stylesheets/style.css" />
<script>
$(document).ready(function() {
$(':text, :password, textarea').funtip();
});
</script>
Related
How do you include your libs into your projects?
Do you use fallback?
for now i'am using my own (i think now the best variant) fallback
function onErrorLoader(obj, link){
if(obj.tagName != "SCRIPT")
obj.href = link;
else
obj.src = link;
}
and i use it like this
First i include my error handler in my html
<script src="js/onErrorLoader.js"></script>
next to this i can call my libs like this
CSS
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" onerror="onErrorLoader(this, 'vendor/bootstrap/dist/css/bootstrap.css')">
JS
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" onerror="onErrorLoader(this, 'vendor/bootstrap/dist/js/bootstrap.js')"></script>
What do you think about this?
What you can advice for make it better, or maybe you know much better way to do this
All this just in case CDN does not reply
For example: I am using my scripts from my own CDN, but in some reasons my CDN does not reply
I've never seen this done. I believe that is because it is unnecessary. I've never seen a scenario where my styles or scripts just, didn't load. Even back when I may have had 12 scripts loaded in the head. The browser just doesn't forget to load the assets.
Most modern workflows have preprocessing and concatenation and then they serve 2 or 3 files. People use task-runners and build tools like grunt, gulp, brunch, broccoli, codekit etc.
My advice would be to just let the browser load the files and not to worry about it, or if you really want to be SURE something happens / look up "promises"
Question: Is there a "built-in" or "easy" way to set the jQuery namespace before the jquery.js script include?
Reason I ask is because I'm working on a script that utilizes the jQuery library, and the script is to go on a page that has a library that uses the $ namespace already. Normally I can just use jQuery.noConflict() except the problem is, there is code on the page (which I cannot control) that hooks into mouse movement events and other stuff that basically triggers calls to the 3rd party code over and over the entire time, so often in fact, that more often than not, js errors are happening between the time the jQuery script is loaded and the .noConflict() call is made. I cannot control or change that 3rd party script.
So basically I need the jQuery object to be instantiated without ever taking $ namespace in the first place. Now.. I'm certain that I could reverse engineer jquery.js and make it not do that, but before I go down that road, I figured surely others have come across this situation.. but I could find no official documentation on jQuery for setting this before the script include; only after. But I figured surely others have come across a problem like this anyways, but I can't seem to find any existing questions detailing this (it could be that I just suck at googling).
Can anybody point me in the right direction?
Edit:
To be clear, this is basically the order in which I need things to happen:
<script src='thirdpartyscript.js'></script>
<script type='text/javascript'>
jQuery.noConflict();
</script>
<script src='jquery.js'></script>
I obviously can't call jQuery.noConflict() before the jquery.js script include, since the jQuery object/method doesn't exist yet.
But I can't call it after the script include, because between time it takes for jquery.js to fully execute and the noConflict call to be made, thirdpartyscript is already throwing errors because jquery took control of $, even for just that one single microsecond or w/e.
So.. I know I can edit jquery.js to never use $ namespace, but I was wondering if there was a built-in way or otherwise easy hack to do it before the jquery.js script include, because a) I don't want to hack jquery.js itself, because I'd like to keep pointing to code.jquery.com instead of maintaining my own instance, b) doing so involves actually figuring out what to change (which in fairness I did a quick eyeballing and it doesn't look like much. mostly my caveat is with point "a")
wont work. The object "jQuery" is not available if you include the lib later. You can create an smart "Watcher" for that.
<script src='thirdpartyscript.js'></script>
<script type='text/javascript'>
// Start an interval
var watch = setInterval(function() {
// Check if jQuery available
if(typeof(jQuery) != 'undefined') {
// Stop the interval
clearInterval(watch);
jQuery.noConflict();
}
}, 500);
</script>
<script src='jquery.js'></script>
I am using jQuery UI and a few other JS libs which in total make for quite a chunk of JS (even minified and combined). My idea is to not include a script tag in the page but to stub out all functions that I defined as well as the $ sign for jQuery so that my inline JS on the page can still call them but will hit the stub. The stub will then load the .js file and actually call the function. The question now is:
How can I redirect all function calls on the window object/global object to a custom function of mine?
I am not used to dynamic languages so a little advice on how to do this in JS will be appreciated.
As stated previously ... this is likely an exercise in futility. Unless you are a researcher and are being paid to do this (and only this), I'd spend my time just working on my actual product and/or refactoring so that the page requires fewer disparate JS libs (for example. use jquery only, rather than jquery + yui)
edit, though, I suppose in the interest of actually answering the question. You can easily replace any function by simply setting it in javascript. For example ...
$ = function(searchString) {
// if this method is called
// and jquery hasn't been loaded yet
// load jquery (which will overwrite all of your local jquery functions with its own
};
The method to lazy load .js files is well documented throughout the web, for example here:
http://ajaxpatterns.org/On-Demand_Javascript
Well the root of your problem is the usage of library dependent in-line JS. We had an old legacy site that had a bunch of in-line JS in the Smarty templates. I ended up modding Smarty so that I could capture the JS calls and then output them all in the footer. Looked something like this
<!-- mySubContent.inc.html -->
<div id="theTabs">
<ul><li><!--
...
--></li></ul>
<div id="tab1"><!--
...
--></div>
</div>
{capture_js}
$("#theTabs").tabs();
{/capture_js}
<!-- footer.inc.html -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
{render_captured_js}
</script>
</body>
</html>
Anyway, maybe that'll give you some idea about how to tackle your in-line JS problem if you can't refactor the codebase right now. Oh, and read this - http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html .
What is the recommended way of including a Javascript file from another Javascript file?
Most people add the JavaScript file to the head of the document:
<script type="text/javascript">
var newfile=document.createElement('script');
newfile.setAttribute("type","text/javascript");
newfile.setAttribute("src", '/myscript.js');
document.getElementsByTagName("head")[0].appendChild(newfile);
</script>
There are libraries that'll do this for you. You can also add a script tag to your document pointing to the file you want to load (from js), which is simplest, but has problems.
http://developer.yahoo.com/yui/yuiloader/
http://www.appelsiini.net/projects/lazyload
Edit: I see a lot of answers that add a script tag to the head of your document. As I said this simple solution has a problem, namely you won't know when the browser has finished loading the script you requested, so you wont know when you can call this code. If you want to use a solution like this you should also add a callback somehow to tell you when the required code was loaded.
jQuery has getScript() function. Also note that Lazy Load mentioned above is only for images. Not for JavaScript files.
$.getScript(url, [callback]);
How about this:
(original link)
<script type="text/javascript">
// Function to allow one JavaScript file to be included by another.
// Copyright (C) 2006-08 www.cryer.co.uk
function IncludeJavaScript(jsFile)
{
document.write('<script type="text/javascript" src="'
+ jsFile + '"></scr' + 'ipt>');
}
</script>
and then to include a second JavaScript file simply add the line:
IncludeJavaScript('secondJS.js');
The page that came from includes some gotchas that arise from this approach, so it's worth looking at before using the method.
Theres also an function built into Scriptaculous that is very easy to use.
Scriptaculous.require("path/to/script.js");
Worth knowing, since Scriptaculous is a very common javascript library these days.
Dojo does it using dojo.require():
dojo.require("your.module.name");
Normally this is a synchronous operation done with XHR. But if you use the xDomain build it will be asynchronous and dojo.addOnLoad() will be raised when the script is loaded.
Read more about it:
Modules
What dojo.require does
Quickstart: custom builds (includes xDomain explanation)
Cross-domain Dojo (specifically dedicated to xDomain stuff).
I am working with both amq.js (ActiveMQ) and Google Maps. I load my scripts in this order
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>AMQ & Maps Demo</title>
<!-- Stylesheet -->
<link rel="stylesheet" type="text/css" href="style.css"></link>
<!-- Google APIs -->
<script type="text/javascript" src="http://www.google.com/jsapi?key=abcdefg"></script>
<!-- Active MQ -->
<script type="text/javascript" src="amq/amq.js"></script>
<script type="text/javascript">amq.uri='amq';</script>
<!-- Application -->
<script type="text/javascript" src="application.js"></script>
</head>
However in my application.js it loads Maps fine but I get an error when trying to subscribe to a Topic with AMQ. AMQ depends on prototype which the error console in Firefox says object is not defined. I think I have a problem with using the amq object before the script is finished loading. Is there a way to make sure both scripts load before I use them in my application.js?
Google has this nice function call google.setOnLoadCallback(initialize); which works great. I'm not sure amq.js has something like this.
cross-domain scripts are loaded after scripts of site itself, this is why you get errors. interestingly, nobody knows this here.
Is there a way to make sure both scripts load before I use them in my application.js?
JavaScript files should load sequentially and block so unless the scripts you are depending on are doing something unusual all you should need to do is load application.js after the other files.
Non-blocking JavaScript Downloads has some information about how scripts load (and discusses some techniques to subvert the blocking).
in jquery you can use:
$(document).ready(function(){/*do stuff here*/});
which makes sure the javascript is loaded and the dom is ready before doing your stuff.
in prototype it looks like this might work
document.observe("dom:loaded", function() {/*do stuff here*/});
If I understand your problem correctly.. I think that may help..
If you don't want to rely on a lib to do this... I think this might work:
<script>
function doIt() {/*do stuff here*/}
</script>
<body onLoad="doIt();"></body>
I had a similar problem to this, only with a single script. The solution I came up with was to use addEventListener("load",fn,false) to a script object created using document.createElement('script') Here is the final function which loads any standard JS file and lets you add a "post load" script.
function addJavaScript( js, onload ) {
var head, ref;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
script = document.createElement('script');
script.type = 'text/javascript';
script.src = js;
script.addEventListener( "load", onload, false );
head.appendChild(script);
}
I hope this may help someone in the future.
Is there a way to make sure both scripts load before I use them?
Yes.
Put the code you want loaded last (your application.js stuff) into prototype's document.observe. This should ensure that the code will load only after prototype + other stuff is finished and ready. (If you are familiar with jQuery, this function is similar to jQuery's $(document).ready )
AMQ depends on prototype which the error console in FireFox says object is not defined.
Do you mean that AMQ depends on the Prototype library? I can't see an import for that library in the code you've provided.
Do you mean that AMQ depends on the
Prototype library? I can't see an
import for that library in the code
you've provided.
Yes for ActiveMQ's javascript (amq.js) does depend on Prototype. In the amq.js it loads 3 scripts, _amq.js, behaviour.js and prototype.js.
Thanks you for your help on the JavaScript load order wrumsby. This tells me that my bug is in another castle :(
I guess I have a different problem. I also checked the js files from ActiveMQ 5.0 to 5.1 and noticed they were the same as well. Something has changed in 5.0 to 5.1 that requires a refresh for the topics to subscribe. I'll keep looking, but thanks for eliminating this possible cause.
You can also use the built in SharePoint javascript method to control the execution of your scripts;
_spBodyOnLoadFunctionNames.push("yourFunction");