Better, Stronger, Safer jQuerify Bookmarklet

A 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

Update: May 7, 2012

Apparently, Safari 5.1 is stricter than other browsers in its enforcement of URL encoding for bookmarklets. The literal "%" needs to be converted to "%25" — something my bookmarklet generator was not doing when it encountered el.style.left='50%'. I've updated the bookmarklet now, so it should work in Safari 5.1 as well as other browsers. ( cf. http://stackoverflow.com/questions/7324022/safari-5-changes-on-use-of-javascript-bookmarklets )

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 $jq as an alias to jQuery for convenience.
    • mention in the flash notice that jQuery is in noConflict mode and that $jq() should be used instead of $().
  • 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.

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.

The script also removes references to script after success now, as suggested below, to avoid a memory leak.

I updated the bookmarklet link above. Here is the updated script:

[js](function() { var el=document.createElement('div'), b=document.getElementsByTagName('body')[0], otherlib=false, msg=''; 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 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(); script.onload = script.onreadystatechange = null; head.removeChild(script); } }; head.appendChild(script); } getScript('http://code.jquery.com/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 { jQuery(el).fadeOut('slow',function() { jQuery(this).remove(); }); if (otherlib) { $jq=jQuery.noConflict(); } } } ,2500); } })();[/js]

Let me know if this one causes any problems.