Better, Stronger, Safer jQuerify Bookmarklet
read 32 commentsA long time ago I built myself a little bookmarklet to load jQuery on pages that don't already have it. The idea was to allow me to play around with any page on the web, using jQuery in the Firebug (and now Safari or IE8) console. I blogged about it, got lots of great feedback, and then blogged about an improved version. Now that a lot more great feedback has come through the comments of the updated bookmarklet post, I've decided to update it one more time.
The Bookmarklet
To use the bookmarklet, drag the following link to your bookmark/favorites list:
» jQuerify «
Then, when you're on a page in which you want to play around with jQuery in the console, just click the bookmarklet.
Problems with the Other One
The biggest problem with the former version was that it didn't work when other libraries that use the $ function, such as Prototype and Mootools, were already loaded on the page. A number of people in the comments suggested that I simply add a jQuery.noConflict(); line to the script. But I wasn't crazy about the idea of having to use jQuery() instead of $() even if no other libraries had been loaded.
Another problem, one that I noticed while testing my new version, was that sometimes the jQuery file that I was inserting from the Google CDN wouldn't fully load by the time I attempted to use it later in the bookmarklet. At least, I think that's what was going on. In any case, it was throwing a "jQuery is not defined" error.
A Few Improvements in This One
To handle the problems, I'm having the script do a few things differently:
- Set up a counter to check 10 times if jQuery is loaded before giving up.
- Use a setTimeout to put a one-quarter-second (250ms) delay interval in between attempts.
- Check if the
$()function is already being used by another library. If it is:- use jQuery's $.noConflict function to release the
$()to the other library. - set
$jqas an alias tojQueryfor convenience. - mention in the flash notice that jQuery is in noConflict mode and that
$jq()should be used instead of$().
- use jQuery's $.noConflict function to release the
- If after multiple attempts jQuery still won't load for some reason, set the flash notice's message accordingly and then remove it through plain old JavaScript rather than jQuery.
Here is the script in its unbookmarkleted form:
- (function() {
- var s=document.createElement('script'),
- el=document.createElement('div'),
- b=document.getElementsByTagName('body')[0];
- var otherlib=false,
- startCounter=tryCounter=10,
- delay=250,
- msg='';
- s.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js');
- el.style.marginLeft='-110px';
- el.style.top='0';
- el.style.left='50%';
- el.style.padding='5px 10px 5px 10px';
- el.style.fontSize='12px';
- el.style.color='#222';
- el.style.backgroundColor='#f99';
- if(typeof jQuery!='undefined') {
- msg='This page already using jQuery v'+jQuery.fn.jquery;
- return showMsg();
- } else {
- if(typeof $=='function') {
- otherlib=true;
- }
- document.getElementsByTagName('head')[0].appendChild(s);
- }
- function showMsg() {
- el.innerHTML=msg;
- b.appendChild(el);
- window.setTimeout(function() {
- if (typeof jQuery=='undefined') {
- b.removeChild(el);
- } else {
- });
- if (otherlib) {
- $jq=jQuery.noConflict();
- }
- }
- } ,2500);
- }
- var tryjQuery=function() {
- setTimeout(function() {
- if (typeof jQuery=='undefined') {
- if (tryCounter) {
- tryCounter--;
- tryjQuery();
- } else {
- msg='Sorry, but after ' + startCounter + ' attempts, jQuery hasn't loaded';
- showMsg();
- }
- } else {
- msg='This page is now jQuerified with v' + jQuery.fn.jquery;
- showMsg();
- }
- }, delay);
- };
- tryjQuery();
- })();
I'm sure it can be improved even further. If you have suggestions for making it more elegant, more efficient, more betterer, I would love to hear them in the comments.
Update
Based on a few comments, I've updated the script one more time to "basically watch the onload and readyState, and fire the callback, thus removing the necessity to poll," as Paul Irish describes below.
Here is the updated link (I also updated it above):
» jQuerify «
And here is the updated script:
- (function() {
- var el=document.createElement('div'),
- b=document.getElementsByTagName('body')[0];
- otherlib=false,
- msg='';
- el.style.marginLeft='-110px';
- el.style.top='0';
- el.style.left='50%';
- el.style.padding='5px 10px 5px 10px';
- el.style.zIndex = 1001;
- el.style.fontSize='12px';
- el.style.color='#222';
- el.style.backgroundColor='#f99';
- if(typeof jQuery!='undefined') {
- msg='This page already using jQuery v'+jQuery.fn.jquery;
- return showMsg();
- } else if (typeof $=='function') {
- otherlib=true;
- }
- // more or less stolen form jquery core and adapted by paul irish
- function getScript(url,success){
- var script=document.createElement('script');
- script.src=url;
- var head=document.getElementsByTagName('head')[0],
- done=false;
- // Attach handlers for all browsers
- script.onload=script.onreadystatechange = function(){
- if ( !done && (!this.readyState
- || this.readyState == 'loaded'
- || this.readyState == 'complete') ) {
- done=true;
- success();
- }
- };
- head.appendChild(script);
- }
- getScript('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js',function() {
- if (typeof jQuery=='undefined') {
- msg='Sorry, but jQuery wasn\'t able to load';
- } else {
- msg='This page is now jQuerified with v' + jQuery.fn.jquery;
- if (otherlib) {msg+=' and noConflict(). Use $jq(), not $().';}
- }
- return showMsg();
- });
- function showMsg() {
- el.innerHTML=msg;
- b.appendChild(el);
- window.setTimeout(function() {
- if (typeof jQuery=='undefined') {
- b.removeChild(el);
- } else {
- });
- if (otherlib) {
- $jq=jQuery.noConflict();
- }
- }
- } ,2500);
- }
- })();
Let me know if this one causes any problems.















My jQuery bookmarklet: http://www.saltarintro.com/wp/2009/03/28/bookmarklets-con-jquery/. (spanish)
Rather than using setTimeout to repeatedly check and see if the script is loaded, you can use the onload event handler for the script itself. When the script loads, the onload event fires for the script element, and you can then notify the user that jQuery has loaded.
Looking good Karl! One suggestion: a small helper function I use quite a lot (when jQuery isn't available) for adding styles to elements:
You're awesome. But then, you already knew that. ;)
Thanks!
Nice stuff! Did you ever checkout the jQuery console?
http://onehackoranother.com/2008/10/jquery-javascript-console-bookmarklet/
I've been using this for a while now.
Glad you like it, Cody - as of a few weeks ago there is a slightly newer version at Github ( http://github.com/jaz303/jquery-console/tree/master ).
(sorry for the hijack, just saw this in my referrer logs :)
Hey Jason,
No problem on the hijack thing. :) I just cloned your console from Github this evening. Great stuff!
@Jason, using the
onloadhandler withSCRIPTelements doesn't work in Internet Explorer or Safari. Here's a cross-browser "lazy-loading" solution: http://ajaxian.com/archives/a-technique-for-lazy-script-loadingWith getting external script from inside a bookmarklet, an emerging standard is to basically watch the onload and readyState, and fire the callback, thus removing the necessity to poll.
Here's some getScript code to consider.
And it's worth noting you're not doing the full
noConflict(true), but I certainly don't mind having both$jqandjQueryavailable.The version number additions are most good; thanks for that. Cheers
I think you can replace the synchronization code by putting most of the script into a seperate file, and loading both jQuery and your script using regular script tags. That way you can rely on the browser to execute both scripts in sequence.
You could also check the version of jQuery that is loaded. Here's how I add jQuery before then loading my own bookmarklet script:
http://www.badlydrawntoy.com/static/960grid/js/960grid-bookmarklet.js
Thanks, everyone, for the excellent suggestions so far!
@sergiomas - looks nice! It doesn't have any of the version messaging, but it's definitely elegant.
@James - that's a great little function. I'm not sure it's worth putting into a bookmarklet if it's only going to be used once, but I really like that sort of thing in general, so thanks!
@Cody - I haven't checked out the jQuery console bookmarklet. But I will now. :)
@Jason, James, Paul Irish, and Jörn Zaefferer - great ideas for avoiding the polling thing. I'll definitely look into all of them.
@Paul Irish - yeah, I figured it wasn't worth doing the extreme noConflict since I'm not loading it if another version already exists. The $jq() is just a convenience, and -- you're right -- either that or jQuery will do.
@Howie, I am checking the version of jQuery.
Just some nitpicks
This is a more reliable way to detect jQuery
if(window.jQuery && jQuery.fn && jQuery.fn.jquery)
And to detect other libraries
if(window.$)
even $ is assigned to a non-function, we should not overwrite to prevent conflict.
Very nice articles on JQuiery loved every bit of it.. thanks for all the help!
Norman Flecha
http://www.straightalk.biz/links/home.php
I just posted my own jQuerify bookmarklet version based on your improved version.
It was just a javascript bookmarklet exercise, but I think it's clean.
http://coder-zone.blogspot.com/2009/05/jquery-bookmarklet.html
Thanks for the great idea and for the code!
Google cannot show this page on top of its results for "jquerify" keyword. You should add a note to the previous posts to point here.
Thanks, Serkan. I added a note.
I'm working on a bookmarklet that popups up a window and grabs some information from the page to submit a form back to my website. I've done this already for the most part with regular old javascript... could I use this code in theory to add jQuery to a page if it doesn't exist and use jQuery for improved UI of the bookmarklet interface? It looks like it, but i wanted to open that question up to the experts. :)
Great bookmarklet! Thanks for explaining in detail, very useful.
It's not working for me. When I drag & drop the bookmarklet into my bookmarks, it doesn't get the full function. If you click on the bookmarklet and copy link location, you can see the failure. It doesn't get the full function, instead it cuts somewhere near head=document.getElementsByTagName("head")[0]
Also the bookmarklet version and the unbookmarkleted version don't seem to be the same. I think there is a problem with the bookmarklet version, because the unbookmarkleted version works perfectly in Firebug command line.
The working bookmarklet for me is;
javascript:(function(){var%20s=document.createElement('script'),el=document.createElement('div'),b=document.getElementsByTagName('body')[0];var%20otherlib=false,startCounter=tryCounter=10,delay=250,msg='';s.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js');el.style.position='fixed';el.style.height='32px';el.style.width='220px';el.style.marginLeft='-110px';el.style.top='0';el.style.left='50%';el.style.padding='5px%2010px%205px%2010px';el.style.fontSize='12px';el.style.color='#222';el.style.backgroundColor='#f99';if(typeof%20jQuery!='undefined'){msg='This%20page%20already%20using%20jQuery%20v'+jQuery.fn.jquery;return%20showMsg();}else{if(typeof%20$=='function'){otherlib=true;}document.getElementsByTagName('head')[0].appendChild(s);}function%20showMsg()%20{el.innerHTML=msg;b.appendChild(el);window.setTimeout(function()%20{if(typeof%20jQuery=='undefined'){b.removeChild(el);}else{jQuery(el).fadeOut('slow',function(){jQuery(this).remove();});if(otherlib){$jq=jQuery.noConflict();}}},2500);}var%20tryjQuery=function(){setTimeout(function(){if(typeof%20jQuery=='undefined'){if(tryCounter){tryCounter--;tryjQuery();}else{msg='Sorry,%20but%20after%20'+startCounter+'%20attempts,%20jQuery%20hasn\'t%20loaded';showMsg();}}else{msg='This%20page%20is%20now%20jQuerified%20with%20v'%20+%20jQuery.fn.jquery;if(otherlib){msg+='%20and%20noConflict().%20Use%20$jq(),%20not%20$().';}showMsg();}},delay);};tryjQuery();})();
So sorry about that. I think WordPress did something funky to the bookmarklet when I updated and re-saved the post last night. I'll fix it as soon as I can.
Sorry again for the problems. It should be good to go now.
Perfect, thx !
Your tutorials and ideas are simply great. Something like you cannot find anywhere else.
Great bookmarklet!
Thanks Karl, I was thinking of making a scriptlet like this for quite a while so that i can play with DOM elements live on site who don't have jQuery. Looks like you've done it right.
This is a very useful bookmarklet. Thank you, Karl.
One minor aesthetics issue I found is that with IE7 and IE8 in quirks mode, the message "This page is now jQuerified" is displayed below the bottom left of the page. This is due to IE not supporting position=fixed in that mode.
To workaround this, I added the following lines after the block of
el.styleassignments:Not sure if this is the best way to detect quirks mode, but other people might be able to come up with something better.
God I love you.
What do you use to convert your JS code from "unbookmarkleted" to "bookmarkleted" form? Thanks.
Hi Kenneth,
I use the JavaScript Tools TextMate bundle, which can be found on GitHub. You can also try the web-based bookmarklet crunchinator.
Thanks for that, I'm using the TextMate bundle now as well.
Just FYI... The code in the the Plain Text version of your lastest code doesn't match the filtered version there is a || missing around line 31 and also the use of double quotes around the head tag causes issues as well which isn't in you previous version.
Fixing those it seems to work like a dream, thanks, Ken.
Thanks for mentioning those problems, Kenneth. Finicky syntax-highlighting plugin. I've updated the code sample, so hopefully the plain text version will match the filtered version now.