I'm looking for the best approach to conditionally load some files based on a specific set of conditions.
I have three CSS files and two javascript files which I'm currently loading like so:
<link href="core.min.css" rel="stylesheet" type="text/css">
<link href="add_regular.min.css" rel="stylesheet" type="text/css">
<link href="add_retina.min.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery-plugin.min.js"></script>
As is evident the fourth file is JQuery and the fifth is my JQuery plugin. Inside the JQuery plugin are a series of functions that are like tests e.g. isMobile, isRetina, isPhone, etc. However, let's focus on isRetina.
What I am trying to accomplish is as follows:
Load JQuery and my JQuery Plugin first
Use isRetina function inside my JQuery plugin to check whether the device has a retina display
Load core.min.css
Load add_regular.min.css if not Retina display, or load add_retina.min.css if Retina display
Currently I'm loading all three CSS files and I wouldn't want to do that because I've got a bunch of other CSS files and I want to load them as my JQuery plugin determines which one is best as per my above example. For example, I might have a phone.min.css that I would want to load after I do another test using my plugin's isPhone function.
I'm considering using YepNope and doing something like this:
<script type="text/javascript" src="yepnope.min.js"></script>
<script>
yepnope([{
load: ['jquery.min.js', 'jquery-plugin.min.js'],
test : $.myplugin.isRetina(),
yep : ['core.min.css', 'add_retina.min.css'],
nope : ['core.min.css', 'add_regular.min.css'],
complete : function(){
//my jquery plugin constructor here
$(selector).myplugin(options);
}
}]);
</script>
However, even if the above approach is the correct one, I am not sure how to implement document.ready, as my plugin's constructor needs to run only when the DOM is ready.
I would like some ideas on how I can pull this off using YepNope or any other solution.
Cheers.
I am not sure how to implement document.ready, as my plugin's constructor needs to run only when the DOM is ready.
There are two relevant events
window.addEventListener('load', function () {
// code runs when document+resources have finished loading
});
and
document.addEventListener('DOMContentLoaded', function () {
// code runs when document has finished parsing, but before resources loaded
});
If you wait for either of these events before importing jQuery, I'm not sure what effect it will have on jQuery's inbuilt $(document).ready / similar.
That said, you do have the option of checking document.readyState === 'complete' before attaching listeners, to see if you should invoke straight away.
I was just going over the JQuery Docs for .ready(), and apparently $(function(){}); is equivalent to $(document).ready(function() {}); (I'm not sure how I'm just discovering this #ScratchingMyHead).
I also saw this being used in a YepNope Tutorial on NetTuts+: http://net.tutsplus.com/tutorials/javascript-ajax/easy-script-loading-with-yepnope-js/.
So I guess my earlier code becoming what you see below should solve the problem:
<script type="text/javascript" src="yepnope.min.js"></script>
<script>
yepnope([{
load: ['jquery.min.js', 'jquery-plugin.min.js', 'core.min.css'],
test : $.myplugin.isRetina(),
yep : 'add_retina.min.css',
nope : 'add_regular.min.css',
complete : function(){
//my jquery plugin constructor added to JQuery DOM Ready Stack
$(function(){
$(selector).myplugin(options);
});
}
}]);
</script>
I will test this out and remove this if it doesn't work.
Not sure if I understand you correctly but this should work.
Include this by default:
<link href="core.min.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery-plugin.min.js"></script>
<script type="text/javascript">
yepnope([{
load: ['jquery.min.js', 'jquery-plugin.min.js'],
test : $.myplugin.isRetina(),
yep : ['core.min.css', 'add_retina.min.css'],
nope : ['core.min.css', 'add_regular.min.css'],
complete : function(){
//my jquery plugin constructor here
$(selector).myplugin(options);
//Remove the mask here.
}
}]);
// Feel free to call ready() here
$(document).ready(function(){
// whatever you want here
});
</script>
Then you can call ready() adding your specific logic according to the output of your tests. This way ready will only fire once the above has loaded and executed.
Related
I offen heard that loading jquery as last element is a good idea because this way a web page loads faster. At the same time I have a script in the header which shows error:
$(document).ready(function () {// Uncaught ReferenceError: $ is not defined
...
}
Should I move jquery loader before the script or I need to change this script some way?
Your concrete issue stems from the fact that you execute statements that use jQuery (i.e. they execute $, which is a function in the jQuery library, also called "the jQuery function" because jQuery is an alias) before it is loaded.
True, it is typically recommended to load scripts last, but that still means the scripts have to be loaded in the correct order, with usually jQuery before your own scripts using jQuery.
If you really want to load your own scripts before jQuery for some reason, you need to defer its execution and have a third helper script to run it, e.g.:
// script.js
(function() {
function myLibraryMainFn() {
$('div').text('simulating work, utilizing jQuery');
}
window.myNamespace = {
run: function() {
myLibraryMainFn()
}
};
}());
<!DOCTYPE html>
<html>
<body>
<div></div>
<script src="script.js"></script>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script>
// Run your script now:
window.myNamespace.run();
</script>
</body>
</html>
Always refer library file first(in your case jQuery), then use it next..For page load and performance add it before body end tags of your HTML
Using the JS loader head.js I'm having a bit of a hard time distinguishing the subtle differences between head.ready() and head.load().
head.ready('jquery.js', function(){//Do something});
VS
head.load('jquery.js', function(){//Do something});
As far as I understand both seem to load 'jquery.js' and then perform a callback when it is loaded. However, in practice I get some edge cases where head.load doesn't work as expected in Firefox making me think I am not understanding where to use head.load and where to use head.ready.
Reading the API it seems like head.load loads the content, but head.ready is an Event Listener, you can also add a callback to head.load and would work too, but head.load is the only one who can actually load the resources, head.ready not.
EDIT: An example
<html>
<head>
<script src="head.min.js"></script>
<script>
// this loads jquery asyncrounously & in parallel
head.load("jquery.min.js");
</script>
</head>
<body>
<!-- some content-->
<!-- injected via a module or an include -->
<script>
// some function that depends on jquery
head.ready("jquery.min.js", function () {
// this will only be executed once jquery has finished loading
$(".main").hide();
});
</script>
<!-- some content-->
</body>
</html>
I'm using the jQuery UI Layout plugin and I keep getting this error in Firebug: $('body').layout is not a function. I also get the same error in IE8 and below.
Obviously, it's being caused by the line where I initiate the layout UI in my scripts file:
$('body').layout({ *options here* });
Is there a way to prevent this error from showing? I'm pretty sure I need the BODY selector for this particular plugin to run.
** SOLUTION **
As the helpful answers say below, I had this line: $('body').layout({ *options here* }); BEFORE I included my jQuery and jQuery UI Layout Plugin files. Once I put the body.layout after those two inclusions, the error went away.
You seem to either
1) have not included the plugin properly (script tag missing/typo in the url, included it before loading jquery itself, whatever else could go wrong)
or
2) calling $("body").layout too early - wrap it with $(document).ready(function() { });
it should be
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.layout.js"></script>
...
<script type="text/javascript">
$(document).ready(function() {
$("body").layout() // will work now
});
</script>
Make sure you're including the lines:
<SCRIPT type="text/javascript" src="/path/to/jquery-latest.js"></SCRIPT>
<SCRIPT type="text/javascript" src="/path/to/jquery.layout-latest.js"></SCRIPT>
Prior to the code you placed in your question. Otherwise, layout will have been undefined before use.
I've been sifting around the web trying to find out whats going on here and I have not been able to get a concrete answer.
I have one $(document).ready on my site that seams to run multiple times regardless of the code that is inside it.
I've read up on the bug reports for jQuery about how the .ready event will fire twice if you have an exception that occurs within your statement. However even when I have the following code it still runs twice:
$(document).ready(function() {
try{
console.log('ready');
}
catch(e){
console.log(e);
}
});
In the console all I see is "ready" logged twice. Is it possible that another .ready with an exception in it would cause an issue? My understanding was that all .ready tags were independent of each other, but I cannot seem to find where this is coming into play?
Here is the head block for the site:
<head>
<title>${path.title}</title>
<meta name="Description" content="${path.description}" />
<link href="${cssHost}${path.pathCss}" rel="stylesheet" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript" charset="utf-8"><!----></script>
<script src="media/js/fancybox/jquery.fancybox.pack.js" type="text/javascript" ><!-- --></script>
<script src="/media/es/jobsite/js/landing.js" type="text/javascript" ><!-- --></script>
<script src="/media/es/jobsite/js/functions.js" type="text/javascript"><!-- --> </script>
<script src="/media/es/jobsite/js/jobParsing.js" type="text/javascript" charset="utf-8"><!----></script>
<script src="/media/es/jobsite/js/queryNormilization.js" type="text/javascript" charset="utf-8"><!----></script>
<script src="${jsHost}/js/jquery/jquery.metadata.js" type="text/javascript" charset="utf-8"><!----></script>
<script src="${jsHost}/js/jquery/jquery.form.js" type="text/javascript" charset="utf-8"><!----></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.7/jquery.validate.min.js" type="text/javascript" charset="utf-8"><!----></script>
<script src="${jsHost}/js/jquery.i18n.properties-min.js" type="text/javascript" charset="utf-8"><!----></script>
<script type="text/javascript" charset="utf-8">
function updateBannerLink() {
var s4 = location.hash.substring(1);
$("#banner").attr('href','http://INTELATRACKING.ORG/?a=12240&c=29258&s4='+s4+'&s5=^');
}
</script>
</head>
Pay no attention to the JSP variables, but as you can see i'm only calling the functions.js file once (which is where the .ready function exists)
The ready event cannot fire twice. What is more than likely happening is you have code that is moving or manipulating the element that the code is contained within which causes the browser to re-execute the script block.
This can be avoided by including script tags in the <head> or before the closing </body> tag and not using $('body').wrapInner();. using $('body').html($('body').html().replace(...)); has the same effect.
It happened to me also, but I realized that the script had been included twice because of a bad merge.
This happened to me when using KendoUI... invoking a popup window would cause the document.ready event to fire multiple times. The easy solution is to set a global flag so that it only runs once:
var pageInitialized = false;
$(function()
{
if(pageInitialized) return;
pageInitialized = true;
// Put your init logic here.
});
It's sort of hack-ish, but it works.
Make sure you don't include JS file twice. That was my case
You might consider to use
window.onload
instead of
$(document).ready
try putting this in your functions.js to prevent it from being executed twice :
var checkit = window.check_var;
if(checkit === undefined){ //file never entered. the global var was not set.
window.check_var = 1;
}
else {
//your functions.js content
}
however i suggest that you look more into it to see where are you calling the second time.
I had a similar problem when I was trying to refresh a partial. I called a return ActionResult instead of a return PartialViewResult. The ActionResult caused my ready() to run twice.
There is a possibility to encounter this problem when you add same controller twice in the html.
For an instance:
[js]
app.controller('AppCtrl', function ($scope) {
$(document).ready(function () {
alert("Hello");
//this will call twice
});
});
[html]
//controller mentioned for the first time
<md-content ng-controller="AppCtrl">
//some thing
</md-content>
//same controller mentioned again
<md-content ng-controller="AppCtrl">
//some thing
</md-content>
I had a similar issue today. A <button type="submit"> caused the $(document).ready(...) event to fire again in my case. Changing the code to <button type="button"> solved the issue for me.
See document.ready function called again after submit button? here on stackoverflow for more details.
In my case $(document).ready was firing twice because of bad CSS, check if any part of your CSS has background-image: url('');
If the iframe doesnt show anything and is used for other reasons (like uploading a file without reload) you can do something like this :
<iframe id="upload_target" name="upload_target" style="width:0;height:0;border:0px solid #fff;"></iframe>
Notice that src is not included that prevents the second on ready trigger on the document.
I had this problem with window.load function was executed twice:
The reason was because I had reference to the same javascript-file in the main page as well as a .net usercontrol. When I removed the reference in the main page, the load-function was only executed once.
I had this happen to me this morning... and what I discovered after closely examining some html code in a jquery modal form that I had recently manipulated, that I'd accidentally removed a closing table tag. I haven't taken the time yet to fully understand why that caused the document.ready function to be called twice, but it did. Adding the closing table tag fixed this issue.
jQuery JavaScript Library v1.8.3 (yes, it is a legacy app)
My problem was that I had tags referencing my JS file in both my index.cshtml file AND my _Layout.cshtml. This was causing the document.ready function to fire twice, which was causing DataTables to bomb.
I am developing an app on a page that uses jQuery 1.2.6, but I would like to use jQuery 1.4.2 for my app. I really don't like to use multiple versions of jQuery like this but the copy on the page (1.2.6) is something I have no control over. I decided to isolate my code like this:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<script type="text/javascript" src="jquery-1.2.6.min.js>
<script type="text/javascript" src="pageStuff.js"></script>
</head>
<body>
Welcome to our page.
<div id="app">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="myStuff.js"></script>
</div>
</body></html>
The file myStuff.js has my own code that is supposed to use jQuery 1.4.2, and it looks like this:
(function($) { //wrap everything in function to add ability to use $ var with noConflict
var jQuery = $;
//my code
})(jQuery.noConflict(true));
This is an extremely simplified version, but I hope you get the idea of what I did. For a while, everything worked fine. However, I decided to use a jQuery plugin in a separate file. I tested it and it acted funny. After some experimentation, I found out that the plugin was using the old version of jQuery, when I wanted it to use the new version. Does anyone know how to import and run a js file from the context within the function wrapping the code in myStuff.js?
In case this matters to anyone, here is how I know the plugin is using the old version, and what I did to try to solve the problem: I made a file called test.js, consisting of this line:
alert($.fn.jquery);
I tried referencing the file in a script tag the way external Javascript is usually included, below myStuff.js, and it came up as 1.2.6, like I expected. I then got rid of that script tag and put this line in myStuff.js:
$.getScript("test.js");
and it still came back as 1.2.6. That wasn't a big surprise -- according to jQuery's documentation, scripts included that way are executed in the global context. I then tried doing this instead:
var testFn = $.proxy($.getScript, this);
testFn("test.js");
and it still came back as 1.2.6. After some tinkering, I found out that the "this" keyword referred to the window, which I assume means the global context. I am looking for something to put in place of "this" to refer to the context of the enclosing function, or some other way to make the code in the file run from the enclosing function. I noticed that if I copy and paste the code, it works fine, but it is a big plugin that is used in many places, and I would prefer not to clutter up my file with their code. I am out of ideas. Does anyone else know how to do this?
When you load jQuery, all it does is to create a root jQuery object and set window.jQuery and window.$ to point to it. So you can simply load the old jQuery, copy window.jQuery to window.jQuery126, load the new jQuery, copy window.jQuery to window.jQuery142, and then change window.jQuery and window.$ whenever you want a plugin to use the other version.
This is an ugly and unstable hack but it should work as long as
all code written for the non-default version is encapsulated with (function($){...})($) (proper jQuery plugins should do this, and you can easily ensure it for your own code)
scripts used with different jQuery versions don't mess with each other's stuff (such as one binding an event handler and the other trying to unbind it)
Another, simpler but less maintainable solution is to simply edit the plugin to look like (function($){...})(jQuery142) instead of (function($){...})(jQuery)
Versions
There is a script that specifically attempts to fix this problem: Versions . It's rather old, though.
It's just a neat helper for doing the switching of versions.
While I have not tested this script myself, you can do something like this based on the GitHub repository for the project:
<!-- Old jQuery and it's plugins -->
<script type="text/javascript" src="../jquery/jquery-1.2.6.js"></script>
<script type="text/javascript" src="test-plugin-for-old.js"></script>
<!-- Include the Versions helper -->
<script type="text/javascript" src="versions.js"></script>
<!-- Include a new jQuery -->
<script type="text/javascript" src="../jquery/jquery.js"></script>
<script type="text/javascript">
Versions.add('jquery', 'latest', jQuery.noConflict(true));
Versions.add('jquery', 'default', jQuery);
</script>
<!-- Load a plugin into the new jQuery -->
<script type="text/javascript">jQuery = Versions.use('jquery', 'latest');</script>
<!-- Do whatever you need with the new jQuery -->
<script type="text/javascript" src="test-plugin-for-new.js"></script>
<!-- Finish up and use the old jQuery back -->
<script type="text/javascript">jQuery = Versions.use('jquery', 'default');</script>
On-Demand JavaScript Loading (Lazy Loading)
With that out of the way, there are some ways to do on-demand JavaScript loading, but I don't think they will work well with loading of jQuery since jQuery modifies and requires the presence of the window object.
Also, you might want to read about script loading. There's more details about it in this StackOverflow question and the article pointed out.
RequireJS
In the list, RequireJS is not mentioned. It's rather new and you can do things like:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script type="text/javascript" src="pageStuff.js"></script>
</head>
<body>
Welcome to our page.
<div id="app">
<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
require({context: "1.4.2"},
["http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"],
function() {
console.log($.fn.jquery); // returns 1.4.2
// myStuff.js
});
require({context: "1.2.6"},
["http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"],
function() {
console.log($.fn.jquery); // returns 1.2.6
});
</script>
</div>
</body></html>
I always go for the newest stuff, haha, so I'm recommending this. However, due to the nature of loading the jquery (it modifies window as mentioned above), all references to $ and jQuery outside and beyond the last require in the HTML code block above, will refer to the jQuery 1.2.6.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" />
<script type="text/javascript">
$new = $.noConflict();
document.write("$new = " + $new.fn.jquery + "<br/>");
document.write("$ = " + $.fn.jquery + "<br/>");
</script>
I just tried this and it seems to work, $ reports to be the old version and you just have to make sure to use $new when invoking jquery for the stuff you want to do with 1.4.2
jQuery is actually a variable in the window context. So whenever you load a jQuery library it will create that variable in the window context if you load it outside any function.
I believe you could do something like this to achieve what you want. Although somewhat restrictive, theoretically it should work:
<script type="text/javascript">
// Get new jQuery code
$.get( "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", function (data, textStatus, XMLHttpRequest){
if(data){
// data should contain the file content. eval() it
eval(data);
// Use jQuery 1.4.2 from here, only inside this callback function.
}
}, "html" );
</script>
This is ugly, but should do the trick.
For now, I am going along with Tgr's solution. Here are the details of how I implemented it:
page.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<script type="text/javascript" src="jquery-1.2.6.min.js>
<script type="text/javascript" src="pageStuff.js"></script>
</head>
<body>
Welcome to our page.
<div id="app">
Here is our app.
<script type="text/javascript">
var oldJQuery = $;
</script>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="myStuff.js"></script>
</div>
</body></html>
myStuff.js:
var jq142 = jQuery.noConflict(true);
(function($) {
var jQuery = $;
$(function() {
//init app stuff
includeScript("jCarousel.js", function() {
//init carousel stuff
});
});
})(jq142);
function includeScript(URL, callback) {
window.$ = window.jQuery = jq142;
window.$.ajax({
url: URL,
dataType: "script",
error: function() {
window.$ = window.jQuery = oldJQuery;
},
success: function() {
if (typeof callback != "undefined")
callback();
window.$ = window.jQuery = oldJQuery;
},
async: false
});
}
I still don't like the idea of putting another variable (jq142) into the global namespace, but I couldn't think of any reasonable way around it. If I come up with something better, I will post it here.