How to load jQuery in isolation? - javascript

I'm needing to load JS code on many different websites with different developer's code, and I want to load in jQuery into their site to manipulate the DOM. The only problem is they may or may not have jQuery already loaded on the page, and I don't want to conflict with that.
A lot of answers deal with checking if jQuery is already loaded on the page, or adding callbacks to when the entire dom is loaded. I don't want to go that approach, instead I want to know if I can directly load jQuery in as it's own variable which is completely isolated, so that I can use it directly without conflicting with whatever is outside.
This includes the fact that they may be using old, specific versions of jQuery that I can't go overwriting with my newer version. I really just want a complete isolated instance.
(function() {
// load jQuery in isolation and call it "xxx"
var divs = xxx('body').find('div');
})();
I also need to do this FROM JS itself, not by editing html (so I can't add jquery script tag in their HTML, i want to load it asyncronously with a callback)

You might try this approach:
(function(xxx) { // add xxx as a paramters
var divs = xxx('body').find('div');
})(jQuery); // Add jQuery as a value here.
As mentioned in other answers. You can check if jQuery is loaded using window.jQuery. And to check the version number you can use jQuery.fn.jquery.
I still however not sure what you mean by isolation. jQuery loads into window object which is global.
You might want to use another library like Zepto which has similar syntax to jQuery, or you can use vanilla JavaScript. See this link http://youmightnotneedjquery.com/

You could do this ...
if(!window.jQuery)
{
// load jquery here
}
Before you load your script. Declare no conflict.
Then you can load your script.
After that is loaded, do ...
var myJq = jQuery.noConflict();
You can access your library using myJq.

Related

Script on parent site not working on child site: $.Deferred is not a function

I have a website that is built off a parent site, and needs to be as identical as possible. My clients have a process through which they grab all the scripts and css files of the parent site and use them to create the child. For one of these websites though, I am getting an error in the main javascript file that is not happening on the parent site.
Here are the two websites:
parent site
child site
The main issue is that the carousel at the top of the page (I believe it's using slickslider) is not getting initialized on the child site. I am not sure if this is because of the Javascript errors, and if you can figure out the reason why it's not working I will be super grateful as that is the main issue at hand. That said, I'm assuming right now that my problem is the javascript error in main.js:
Uncaught TypeError: l.Deferred is not a function
Again, there are no errors on the parent site, but l.Deferred breaks on the child site. Why isn't it working?
I tried wrapping l in jQuery -- $(l).Deferred -- but when I did this it seemed to cause a loop, where the page would spend several minutes trying to load before timing out and crashing.
If you can tell me why the slider on my child site isn't working, or at the very least why l.Deferred is breaking, that would be a huge help.
EDIT: Update to use the correct version of jquery:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>jq11 = jQuery.noConflict(true);</script>
<script src="/sites/all/themes/wma2/js/main.js"></script>
(function($){
window.matchMedia || (window.matchMedia = function(){....
})(jq11);
UPDATE: I checked the console and $.fn.jQuery is using the jquery 1.11.2 at the point when it says l.Deferred is not a function
I also tried wrapping l in the correct jquery:
jq11(l).Deferred
but when I do that the page gets stuck in a loop and crashes after trying to load for several minutes.
I'm not 100% sure what "process through which they grab all the scripts and css files of the parent site and use them to create the child" means, but the client page is at least at some point loading jQuery 1.4.4 (check the first of the last 3 included scripts in the head).
$.Deferred wasn't introduced until jQuery 1.5, https://api.jquery.com/jquery.deferred/ so trying to run anything using 1.4.4 will throw an error because that function, indeed, doesn't exist there.
One of the last scripts on the page loads another entire copy of jQuery, and THAT one, 1.11, does support Deferreds. But it's almost the last thing to run on the page, so almost no code uses it. So you have an old jQuery, then code that tries to use a method in a newer jQuery, and then finally a newer jQuery. That's why jQuery and $ aren't even assigned to the same thing, as you can confirm in the console:
jQuery.fn.jquery: "1.4.4"
$.fn.jquery: "1.11.2"
It's possible that you have code that uses old 1.4.4 syntax, so you may not be able to replace it, but it's probably best, if you do need both versions, to wrap all your code explicitly in an IIFE
(function($){
/* code using $, which maps to whatever is specified at the end*/
})(jQuery);
that specifies which of the two you mean to use.

Correct way to unify separate js files with several jquery (document).ready() events

I have a very large project that has several external javascript files that I would like to unify in a single large one to optimize server load.
The problem is that many of these scripts have they own (document).ready() events that targets element that are only available on that page.
Unifying everything would put a lot of (different) (document).ready() in a single file and most of them will target elements that are not on the page, is this a problem?
What is the correct way to approach this?
EDIT: Clarification: I mean unify hundreds of javascript file included in they own tag in a single file, so I can minify it.
It sounds to me like you have a different javascript file included on each page and you want to consolidate them together.
If that is what you are trying to do then the simplest way is to just concatenate all the javascript into a single file. It is fine to have multiple ready calls in the same file. Also jQuery should not error if elements are not present on the page so long as you use jQuery methods to do things (for example $('.element').show() would be fine, but $('.element')[0].style.display = 'block'; would error if the element is not present on that page.
Putting them all together could have unexpected consequences though if you have things that you only want to apply to specific pages. One way to handle this would be to check which page you are on before attaching specific events. You can do this by checking for specific elements on the page like
// check if element is present and visible
if ($('.element').is(':visible')) {
// now we are on this specific page so let's do everything
// specific to this page here
}
or
// checks for presence of element
if ($('.element').length) {
}
Or you could use an id or other means to differentiate.
You could now make one $(document).ready(init); call at the bottom of your javascript and the init function could decide what needs to be initialized for that page.
Hope this helps!
I'm not quite clear on the question (where some of the elements are not on the page) but jQuery is specifically designed to have multiple $(function () {}) (short cut for document ready) calls in a page and have them run at the correct time.
I tend to add a check in to see if the elements I want jQuery to interact with actually exist before I start using them.
Having said that, a lot of standard jQuery stuff will just silently fail if the elements it's operating on aren't there, which is usually the desired behaviour. There are a few plugins though that output warnings to the console if they fail. On modern browsers this is not a problem, but on IE without the debug bar installed, it results in a full error, which is obviously not desired at all.
You don't need to have multiple (document).ready() blocks, you should be able to keep all of your code inside a single (document).ready() block. The code will target only the items that correspond to the selector, so you should be fine.
On a side note, you need to make sure you won't have different objects from different pages which have the same id, class or other attributes - if a single JS file corresponds to all of these pages and there are multiple elements on different pages that have the attribute that your JS selector matches with, you'll have un-desired (if the project is too complex, hard-to-identify) side-effects.

Insert Jquery after another script

i want to insert Jquery library in my HTML, but i can't place it on the header of file. I need to include it after some scripts im running.
Do you know any way to do this without getting an error?
You may be trying to call jQuery in a script before your jQuery library file is loaded.
You only have to load jQuery before your scripts that use it. Other scripts that don't use jQuery can go before it if you want.
For example, if you want to include jQuery in a script tag at the end of the body and then include some scripts that use jQuery after that, you can do it that way.
If you are constructing your own site/page then you may be able to control this enough by making sure jQuery is included before the other files. However, if you are using something like WordPress or other dynamic CMS platforms you may not be able to stop something from referencing jQuery before it exists.
Check out this github project for jqShim that might help. You can include the very basic dom ready functionality in the head while moving the bigger/larger/slower/complete jQuery download to the bottom of the BODY (but before any plugins). As long as the code that calls jQuery is doing something like:
jQuery(function($) { ... doing something when ready ... });
Those calls can still be made in the body, after using the jqShim, and will behave as expected. Let me know if it proves to be helpful!

How to dynamically add a Javascript function (and invoke)

Based on a click event on the page, via ajax I fetch a block of html and script, I am able to take the script element and append it to the head element, however WebKit based browsers are not treating it as script (ie. I cannot invoke a function declared in the appended script).
Using the Chrome Developer Tools I can see that my script node is indeed there, but it shows up differently then a script block that is not added dynamically, a non-dynamic script has a text child element and I cannot figure out a way to duplicate this for the dynamic script.
Any ideas or better ways to be doing this? The driving force is there is potentially a lot of html and script that would never be needed unless a user clicks on a particular tab, in which case the relevant content (and script) would be loaded. Thanks!
You could try using jQuery... it provides a method called .getScript that will load the JavaScript dynamically in the proper way. And it works fine in all well known browsers.
How about calling eval() on the content you receive from the server? Of course, you have to cut off the <script> and </script> parts.
If you're using a library like jQuery just use the built-in methods for doing this.
Otherwise you'd need to append it to the document rather than the head like this:
document.write("<scr" + "ipt type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js\"></scr" + "ipt>");
In all honesty, I have no idea why the script tag is cut like that, but a lot of examples do that so there's probably a good reason.
You'll also need to account for the fact that loading the script might take quite a while, so after you've appended this to the body you should set up a timer that checks if the script is loaded. This can be achieved with a simple typeof check on any global variable the script exports.
Or you could just do an eval() on the actual javascript body, but there might be some caveats.
Generally speaking though, I'd leave this kind of thing up to the browser cache and just load the javascript on the page that your tabs are on. Just try not to use any onload events, but rather call whatever initializers you need when the tab is displayed.

JavaScript - controlling the insertion point for document.write

I would like to create a page that runs a 3rd party script that includes document.write after the DOM was already fully loaded.
My page is not XHTML. My problem is that the document.write is overwriting my own page. (which is what it does once the DOM was loaded).
I tried overriding the document.write function (in a way similiar to http://ejohn.org/blog/xhtml-documentwrite-and-adsense/) but that doesn't cover cases where the document.write contains partial tags.
An example that would break the above code is:
document.write("<"+"div");
document.write(">"+"Done here<"+"/");
document.write("div>");
Is there some way to modify the document.write insertion point through JavaScript? Does anyone have a better idea how to do this?
If you're dealing with 3rd party scripts, simply replacing document.write to capture the output and stick it in the right place isn't good enough, since they could change the script and then your site would break.
writeCapture.js does what you need (full disclosure: I'm the author). It basically rewrites the script tags so that each one captures it's own document.write output and puts it in the correct place. The usage (using jQuery) would be something like:
$(document.body).writeCapture().append('<script type="text/javascript" src="http://3rdparty.com/foo.js"></script>');
Here I'm assuming that you want to append to the end of the body. All jQuery selectors and manipulation methods will work with the plugin, so you can inject it anywhere and however you want. It can also be used without jQuery, if that is a problem.
It is possible to override the document.write method. So you can buffer the strings sent to document.write and output the buffer wherever you like. However changing a script from synchronous to asynchronous can cause errors if not handled correctly. Here's an example:
Simplified document.write replacement
(function() {
// WARNING: This is just a simplified example
// to illustrate a problem.
// Do NOT use this code!
var buffer = [];
document.write = function(str) {
// Every time document.write is called push
// the data into buffer. document.write can
// be called from anywhere, so we also need
// a mechanism for multiple positions if
// that's needed.
buffer.push(str);
};
function flushBuffer() {
// Join everything in the buffer to one string and put
// inside the element we want the output.
var output = buffer.join('');
document.getElementById("ad-position-1").innerHTML = output;
}
// Inject the thid-party script dynamically and
// call flushBuffer when the script is loaded
// (and executed).
var script = document.createElement("script");
script.onload = flushBuffer;
script.src = "http://someadserver.com/example.js";
})();
Content of http://someadserver.com/example.js
var flashAdObject = "<object>...</object>";
document.write("<div id='example'></div>");
// Since we buffer the data the getElementById will fail
var example = document.getElementById("example");
example.innerHTML = flashAdObject; // ReferenceError: example is not defined
I've documented the different problems I've encountered when writing and using my document.write replacement: https://github.com/gregersrygg/crapLoader/wiki/What-to-think-about-when-replacing-document.write
But the danger of using a document.write replacement are all the unknown problems that may arise. Some are not even possible to get around.
document.write("<scr"+"ipt src='http://someadserver.com/adLib.js'></scr"+"ipt>");
adLib.doSomething(); // ReferenceError: adLib is not defined
Luckily I haven't come across the above problem in the wild, but that doesn't guarantee it won't happen ;)
Still want to try it out? Try out crapLoader (mine) or writeCapture:
You should also check out friendly iframes. Basically it creates a same-domain iframe and loads everything there instead of in your document. Unfortunately I haven't found any good libraries for handling this yet.
Original Answer before the edit:
Basically the problem of document.write is that it does not work in XHTML documents. The most broad solution then (as harsh as it may seem) is to not use XHTML/xml for your page. Due to IE+XHTML and the mimetype problem, Google Adsense breaking (may be a good thing :), and the general shift towards HTML5 I don't think it's as bad as it seems.
However if you'd really like to use XHTML for your page, then John's script that you linked to is the best you've got at this point. Just sniff for IE on the server. If the request is from IE, don't do anything (and don't serve the application/xhtml+xml mimetype!). Otherwise drop it into the <head> of your page and you're off to the races.
Re-reading your question, is there a specific problem you have with John's script? It is known to fail in Safari 2.0 but that's about it.
You may be interested in the Javascript library I developed which allows to load 3rd party scripts using document.write after window.onload. Internally, the library overrides document.write, appending DOM elements dynamically, running any included scripts which may use document.write as well.
Unlike John Resig's solution (which was part of the inspiration for my own code), the module I developed supports partial writes such as the example you give with the div:
document.write("<"+"div");
document.write(">"+"Done here<"+"/");
document.write("div>");
My library will wait for the end of the script before parsing and rendering the markup. In the above example, it would run once with the full string "<div>Done here</div>" instead of 3 times with partial markup.
I have set up a demo, in which I load 3 Google Ads, an Amazon widget as well as Google Analytics dynamically.
FWIW, I found postscribe to be the best option out there these days - it handles wrapping a pesky ad rendering module like a charm allowing our page to load without being blocked.
In order to alter the content of the page after the DOM has rendered you need to either use a javascript library to append HTML or text at certain points (jQuery, mootools, prototype, ...) or just use the innerHTML property of each DOM element to alter/append text to it. This works crossbrowser and doesn't require any libraries.
There are better ways to do this.
2 ways
1) Append
<html><head>
<script>
window.onload = function () {
var el = document.createTextNode('hello world');
document.body.appendChild(el);
}
</script></head><body></body></html>
2) InnerHTML
<html><head><script>
window.onload = function () {
document.body.innerHTML = 'hello world';
}
</script></head><body></body></html>

Categories

Resources