Better, Stronger, Safer jQuerify Bookmarklet

read 32 comments

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

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.

Here is the script in its unbookmarkleted form:

JavaScript:
  1. (function() {
  2.   var s=document.createElement('script'),
  3.       el=document.createElement('div'),
  4.       b=document.getElementsByTagName('body')[0];
  5.   var otherlib=false,
  6.       startCounter=tryCounter=10,
  7.       delay=250,
  8.       msg='';
  9.   s.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js');
  10.   el.style.position='fixed';
  11.   el.style.height='32px';
  12.   el.style.width='220px';
  13.   el.style.marginLeft='-110px';
  14.   el.style.top='0';
  15.   el.style.left='50%';
  16.   el.style.padding='5px 10px 5px 10px';
  17.   el.style.fontSize='12px';
  18.   el.style.color='#222';
  19.   el.style.backgroundColor='#f99';
  20.  
  21.   if(typeof jQuery!='undefined') {
  22.     msg='This page already using jQuery v'+jQuery.fn.jquery;
  23.     return showMsg();
  24.   } else {
  25.     if(typeof $=='function') {
  26.       otherlib=true;
  27.     }
  28.     document.getElementsByTagName('head')[0].appendChild(s);
  29.   }
  30.   function showMsg() {
  31.     el.innerHTML=msg;
  32.     b.appendChild(el);
  33.     window.setTimeout(function() {
  34.       if (typeof jQuery=='undefined') {
  35.         b.removeChild(el);
  36.       } else {
  37.         jQuery(el).fadeOut('slow',function() {
  38.           jQuery(this).remove();
  39.         });
  40.         if (otherlib) {
  41.           $jq=jQuery.noConflict();
  42.         }
  43.       }
  44.     } ,2500);    
  45.   }
  46.   var tryjQuery=function() {
  47.     setTimeout(function() {
  48.       if (typeof jQuery=='undefined') {
  49.         if (tryCounter) {
  50.           tryCounter--;
  51.           tryjQuery();
  52.         } else {
  53.           msg='Sorry, but after ' + startCounter + ' attempts, jQuery hasn't loaded';
  54.          showMsg();
  55.        }
  56.      } else {
  57.        msg='This page is now jQuerified with v' + jQuery.fn.jquery;
  58.        if (otherlib) {msg+=' and noConflict(). Use $jq(), not $().';}
  59.        showMsg();
  60.      }
  61.    }, delay);
  62.  };
  63.  tryjQuery();
  64. })();

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:

JavaScript:
  1. (function() {
  2.   var el=document.createElement('div'),
  3.       b=document.getElementsByTagName('body')[0];
  4.       otherlib=false,
  5.       msg='';
  6.   el.style.position='fixed';
  7.   el.style.height='32px';
  8.   el.style.width='220px';
  9.   el.style.marginLeft='-110px';
  10.   el.style.top='0';
  11.   el.style.left='50%';
  12.   el.style.padding='5px 10px 5px 10px';
  13.   el.style.zIndex = 1001;
  14.   el.style.fontSize='12px';
  15.   el.style.color='#222';
  16.   el.style.backgroundColor='#f99';
  17.  
  18.   if(typeof jQuery!='undefined') {
  19.     msg='This page already using jQuery v'+jQuery.fn.jquery;
  20.     return showMsg();
  21.   } else if (typeof $=='function') {
  22.     otherlib=true;
  23.   }
  24.  
  25.   // more or less stolen form jquery core and adapted by paul irish
  26.   function getScript(url,success){
  27.     var script=document.createElement('script');
  28.     script.src=url;
  29.     var head=document.getElementsByTagName('head')[0],
  30.         done=false;
  31.     // Attach handlers for all browsers
  32.     script.onload=script.onreadystatechange = function(){
  33.       if ( !done && (!this.readyState
  34.            || this.readyState == 'loaded'
  35.            || this.readyState == 'complete') ) {
  36.         done=true;
  37.         success();
  38.       }
  39.     };
  40.     head.appendChild(script);
  41.   }
  42.   getScript('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js',function() {
  43.     if (typeof jQuery=='undefined') {
  44.       msg='Sorry, but jQuery wasn\'t able to load';
  45.     } else {
  46.       msg='This page is now jQuerified with v' + jQuery.fn.jquery;
  47.       if (otherlib) {msg+=' and noConflict(). Use $jq(), not $().';}
  48.     }
  49.     return showMsg();
  50.   });
  51.   function showMsg() {
  52.     el.innerHTML=msg;
  53.     b.appendChild(el);
  54.     window.setTimeout(function() {
  55.       if (typeof jQuery=='undefined') {
  56.         b.removeChild(el);
  57.       } else {
  58.         jQuery(el).fadeOut('slow',function() {
  59.           jQuery(this).remove();
  60.         });
  61.         if (otherlib) {
  62.           $jq=jQuery.noConflict();
  63.         }
  64.       }
  65.     } ,2500);    
  66.   }
  67. })();

Let me know if this one causes any problems.

comment feed

32 comments

  1. 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.

  2. Looking good Karl! One suggestion: a small helper function I use quite a lot (when jQuery isn't available) for adding styles to elements:

    function applyStyles( elem, styles ){
        for (var prop in styles) {
            elem.style[prop] = styles[prop];
        }
    }
    
    // Usage:
    applyStyles( el, {
        position: 'fixed',
        height: '32px',
        width: '220px',
        marginLeft: '-110px',
        top: '0',
        left: '50%',
        padding: '5px 10px 5px 10px',
        fontSize: '12px',
        color: '#222',
        backgroundColor: '#f99'
    });
    
  3. WC

    You're awesome. But then, you already knew that. ;)

    Thanks!

  4. 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.

  5. @Jason, using the onload handler with SCRIPT elements 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-loading

  6. With 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 $jq and jQuery available.

    The version number additions are most good; thanks for that. Cheers

  7. 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.

  8. 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

  9. 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.

  10. 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.

  11. Norman Flecha "STRAIGHTALK"

    Very nice articles on JQuiery loved every bit of it.. thanks for all the help!

    Norman Flecha
    http://www.straightalk.biz/links/home.php

  12. 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!

  13. Serkan

    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.

  14. 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. :)

  15. Great bookmarklet! Thanks for explaining in detail, very useful.

  16. 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();})();

  17. Nico

    Perfect, thx !

  18. Your tutorials and ideas are simply great. Something like you cannot find anywhere else.

  19. 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.

  20. Ken Lin

    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.style assignments:

        var m = document.compatMode;
        if (m) {
            if (m == 'BackCompat') {
                el.style.position = 'absolute';
                el.style.top = b.scrollTop;
            }
        }

    Not sure if this is the best way to detect quirks mode, but other people might be able to come up with something better.

  21. Kenneth Lee

    What do you use to convert your JS code from "unbookmarkleted" to "bookmarkleted" form? Thanks.

21 Pings

  1. [...] Better, Stronger, Safer jQuerify Bookmarklet [...]

  2. [...] matching your CSS selectors or XPath expressions. Nice! As an alternative one could of course hook jQuery via a bookmarklet and then type some selector s(h)izzle into the regular console. Spread the [...]

  3. [...] read 38 comments by Karl Swedberg For the most recent version of the bookmarklet, see the Better, Stronger, Safer jQuerify Bookmarklet [...]

  4. [...] see my entry about the updated and improved bookmarklet Better, Stronger, Safer jQuerify Bookmarklet. comment [...]

  5. [...] via Better, Stronger, Safer jQuerify Bookmarklet » Learning jQuery - Tips, Techniques, Tutorials. [...]

  6. [...] jQuerify is a simple way to inject the jQuery framework into any website and enable immediate DOM manipulation. To try it out, just drag this link to your bookmark list. Depending on the browser you’re using, you can then enter any javascript commands via the console window in Firebug, IE 8 Developer Tools, or Chrome Developer Tools. [...]

  7. [...] into everyday websites. Keep in mind you may need to either inject jQuery with a bookmarklet (link) and the De-pagify [...]

  8. [...] Shared Better, Stronger, Safer jQuerify Bookmarklet » Learning jQuery – Tips, Techniques, Tutorials [...]

  9. [...] the jQueryify bookmarklet is integrated into FireQuery which makes injecting jQuery into any website [...]

  10. [...] a jQueryify bookmarklet is integrated in to FireQuery that creates injecting jQuery in to any website [...]

  11. [...] 在learningjquery上面讲到了关于jQuerify Bookmarklet ,使用它可以快速的为任何一个网页加入Jquery库,从而可以做任何你想做的事情。 [...]

  12. [...] 原文:Better, Stronger, Safer jQuerify Bookmarklet作者:Karl Swedberg [...]

  13. [...] el jQueryify bookmarklet está integrado a FireQuery permitiendo inyectar código de jQuery en cualquier sitio [...]

  14. [...] see embedded data elements and event handlers that were added via jQuery. Also, the familiar jQuerify Bookmarklet has been built into Firebug allowing you to inject jQuery into pages that didn’t have it [...]

  15. [...] see embedded data elements and event handlers that were added via jQuery. Also, the familiar jQuerify Bookmarklet has been built into Firebug allowing you to inject jQuery into pages that didn’t have it [...]

  16. [...] mich freuen wenn sich das SEOQuake-Team, Karl Swedberg (jQuerify), David Naylor (Playground) und all die anderen JavaScript-Schreiberlinge daran ein Beispiel nehmen [...]

  17. [...] a comment » jQuerify is a bookmarklet that can dynamically add jQuery to any page, in case you wanted to use it through [...]

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.