Adding icons to bookmarklets … again

Show simple Demo

Yesterday I got a question on how to make my Bookmarklet-with-icon code work and sadly, it seems to be broken now. So I set out to find another method and strangely enough the solution that works with modern Firefox versions is quite simple and elegant:

Open the bookmarklet in "page mode" once after the user has added it as bookmarklet. This may sound like something that’s terribly hard to detect, but with a few simple tricks it’s not much of an issue: Mainly because you can make it so that the user barely notices if it fires to often.

  1. Just like the original bookmarklet-with-icon code, adding icons still relies on the behavior that a javascript: url which returns a string will cause the browser to display that string as a HTML document. So, the first step (again) is saving the HTML string somewhere. Of course, you can still add a bit of randomness, but for simplicity’s sake we’ll leave it out for now:

    <script>
      top["bookmarkletv2sample@tapper-ware.net"] = ''
        +'<!DOCTYPE html>'
        +'<html>'
          +'<head>'
            +'<title>My Bookmarklet</title>'
          +'</head>'
          +'<body></body>'
        +'</html>'
      ;
    </script>

  2. Now, we add the magic favicon sauce:

    <script>
      top["bookmarkletv2sample@tapper-ware.net"] = ''
        +'<!DOCTYPE html>'
        +'<html>'
          +'<head>'
            +'<title>My Bookmarklet</title>'
            +'<link rel="icon" href="http://www.my.domain/favicon.ico" />'
          +'</head>'
          +'<body></body>'
        +'</html>'
      ;
    </script>

  3. Like I said, we want that page to only appear very briefly, so we add a history.back(); call that will take us right back to the previous page. I’ve added a timeout as well, which is probably not necessary, but with hacks like that it’s better to err on the side of caution. It’s also a good idea to split the closing script tag to prevent any browser from misinterpreting it as the end of the current script block:

    <script>
      top["bookmarkletv2sample@tapper-ware.net"] = ''
        +'<!DOCTYPE html>'
        +'<html>'
          +'<head>'
            +'<title>My Bookmarklet</title>'
            +'<link rel="icon" href="http://www.my.domain/favicon.ico" />'
          +'</head>'
          +'<body>'
            +'<script>'
              +'window.onload=function(){'
                +'window.setTimeout(function(){'
                  +'history.back();'
                +'},250);'
              +'};'
            +'</scr'+'ipt>'
          +'</body>'
        +'</html>'
      ;
    </script>

  4. And that’s pretty much it for our magic assign-icon-from-within-bookmarklet page. Now we just have to change the actual bookmarklet to return that HTML string instead of running the bookmarklet code whenever a thusly-named HTML string exists on the current page. Assuming our bookmarlet is a plain old alert("This is my bookmarklet");void(0);, we have to wrap it into a little if-block like this:

    if(top["bookmarkletv2sample@tapper-ware.net"]){
       top["bookmarkletv2sample@tapper-ware.net"];
    }else{	
       alert('This is my bookmarklet');
      void(0);
    }


    which, written as a bookmarklet one-liner, turns into

    <a href="javascript:if(top['bookmarkletv2sample@tapper-ware.net']){top['bookmarkletv2sample@tapper-ware.net'];}else{alert('This%20is%20my%20bookmarklet');void(0);}">My Bookmarklet</a>

  5. If you put those blocks somewhere on your page it will already work… sort of: If you drag the link to your bookmarks-toolbar, then click it, you’ll briefly see an empty page and the icon will appear. Now, all we have to automate is that one click. Luckily, all we have to do for that is listen for a dragend event, which occurs whenever the user drags the link anywhere. It also occurs during any other drag operation but since that brief refresh is barely noticeable it doesn’t really matter if we try to assign the icon too often. All we have to do for that is add an ondragend attribute to the a element:

    ondragend="this.click();"


    Which finally turns our “A” element into this:

    <a ondragend="this.click();" href="javascript:if(top['bookmarkletv2sample@tapper-ware.net']){top['bookmarkletv2sample@tapper-ware.net'];}else{alert('This%20is%20my%20bookmarklet');void(0);}">My Bookmarklet</a>

And that’s all there is to it. Include the link and the script element on your page, tell the users that they should add it by dragging the link and that it won’t work on the current page, adjust favicon-url and exchange the example with your own JS code. Done.