Thursday, December 17, 2009

Show Anchors Bookmarklet

Show Anchors
(version 2.2, updated 6/30/2010)
How to use this bookmarklet:
  1. Install the bookmarklet in your browser (e.g. by dragging the "Show Anchors" link from above into your browser's bookmarks).
  2. Navigate to a page which has anchors (e.g. a Wikipedia article that contains a table of contents).
  3. Invoke the bookmarklet by clicking the bookmark you added from step 1.
    You will now see a lot of anchor icons located near the anchors on the webpage you are viewing.
  4. Do one of the following to copy a link to an anchor's location:
    • Click on one to navigate to that location, and then copy the location from the address bar.
    • Right-click on an icon and copy its location.

One thing that really annoys me on the internet is when you get to some really big page and find something useful in the middle of the page. You want to link to it, but you can't. If you are lucky, the site owner implemented anchors which allow you to jump to a specific section of the page which does allow you to link to that section. However there are many times I come across webpages that don't have links to the different sections. Some examples are forum posts (mainly non-standard forum engines) and comments on blogs and sites such as YouTube. The annoying part is that in the source code, there are elements which allow you to link to that specific part of the page, it's just that the site owners didn't provide a means to access it easily. So instead of having to spend a minute or two every time (i.e. viewing the source code, finding the anchor to use, etc), I thought that a bookmarklet would be an excellent solution.

The first thing I did was try to look for any existing solution. The top 10 results in Google all weren't good. Many of them were outdated (read: don't work), didn't support linking to the ID attribute. On the W3 specification page for anchors you can see that it says that the ID attribute can be used as anchors:
Destination anchors in HTML documents may be specified either by the A element (naming it with the name attribute), or by any other element (naming with the id attribute).
This makes many more anchor locations available on webpages... places that the site owner probably never intended to be linked to. In fact, I just used this very bookmarklet to find a nice place to link to in that document which takes you as close to the quoted text as possible. The only problem would be if they change the page thinking no one linked to those ID attributes, but that's a risk I'm willing to take.

So since none of the existing solutions worked, I decided to make my own. There were a few requirements:

  1. Not hosted on a 3rd party server (i.e. the bookmarklet should not simply import a JavaScript file from a site that may or may not be up).
  2. Images should be used to show where the links are. This makes it much easier to spot than text. Btw, the image should not be hosted on a website either.
  3. And finally, it should work exactly as the specification states, where all A tags with name attributes and all elements with ID attributes (except for hidden elements) are linkable.

The first idea I had was to use JQuery. JQuery is a JavaScript library that makes programming in JavaScript fun. In fact, I was able to write most of the code needed in essentially 3 lines. This fit requirement #1 very well because it needs to be as small as possible if it's a non-hosted bookmarklet. While I am linking to the current version of JQuery, which adds a dependency, I would much rather prefer adding a dependency on a well known library such as JQuery than something else. It's more likely that the web server will be up than if I used a different hosted solution. The benefits (3 lines of code with cross compatibility across all browsers) far outweighed the drawback (dependency).

To load JQuery, I used a modified version of the code here. The only difference is that I reduced the function and variable names to use up less characters. In case you want to make your own bookmarklet that uses JQuery, you can use this template:

  function ls(u, c) {
    var h = document.getElementsByTagName("head")[0];
    var s = document.createElement("script");
    s.src = u;
    var d = false;
    s.onload = s.onreadystatechange = function() {
      if(!d && ( !this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
        d = true;
        s.onload = s.onreadystatechange = null;

  var $;
  ls("", function() {
    $ = jQuery;
    // do something

Don't forget to use a site such as Bookmarklet Builder to compress the whitespace even further.

Now that I had JQuery loading correctly, and figuring out the code to write was simple. I just needed to get the image to use. I decided to use the anchor image from the famfamfam set of icons. In following the requirement to not have a hosted image, I used this Base64 converter and converted it to base64 so it is essentially embedded into the bookmarklet. Another benefit of not using a hosted image is that it loads really quickly when compared to a hosted solution.

I tested the bookmarklet on Safari (Mac) and Chrome (PC), so while I didn't test on all the 5 major browsers, it should work flawlessly on all of them because it is using JQuery. I find this bookmarklet very useful and a time saver. Heck, it even helped me writing this blog post! But there are other benefits as well such as not having to find the links on websites. Each website can have anchor links in different places. With this bookmarklet, you get the same icon in exactly the place where the anchor appears making it much easier to use.

The first line in the bookmarklet contains the version number, this way if I ever update it, you can know if you have the latest version or not. To install the bookmarklet, grab it from the top of this post. Most browsers let you drag and drop it onto your bookmarks. If you need more help installing it, search Google for how to install a bookmarklet in your browser.

I hope you find it as useful as I do!


  1. Hi, very elegant and works like a charm!
    I used the developer tools for this in Firefox but found no solution for Chrome until before.
    Thank you so much!

  2. Thanks for this! It's perfect and simple. Exactly what I would have built if I couldn't find a decent solution, but you saved me the time!

  3. Just FYI this is working nicely in chrome on OS X.

  4. Nice, but I had a couple of problemes. In my version the images are styled a bit to make them easier to see and stop moving stuff around. I also added special handling of table cells with id's to stop breaking tables.

    javascript:(function (){/* v2.2+ -- */function ls(u,c){var h=document.getElementsByTagName("head")[0];var s=document.createElement("script");s.src=u;var d=false;s.onload=s.onreadystatechange=function (){if(!d&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){d=true;c();s.onload=s.onreadystatechange=null;h.removeChild(s);}};h.appendChild(s);}var $;var loc=location.href;var anchorPos=location.href.lastIndexOf('#');if(anchorPos>-1){loc=loc.substring(0,anchorPos);/* if already has an anchor, it needs to be replaced */}function a(n,t){return '<a href="'+loc+'#'+n+'" title="'+t+': '+n+'" style="background-color: #fff; border: 1px solid black; display: block; position: absolute;"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGdSURBVDjLpVMxa8JAFL6rAQUHXQoZpLU/oUOnDtKtW/MDBFHHThUKTgrqICgOEtd2EVxb2qFkKTgVChbSCnZTiVBEMBRLiEmafleCDaWxDX3w8e7dve+7l3cv1LZt8h/jvA56vV7DNM20YRgE/jyRSOR+ytvwEgAxvVwui/BF+LTvCtjNwKvj/X8CbgXPOHMEZl559HsTu93uPQi7jBiNRgMEx8PR0GIxRB+y2eze2gqQeAXoSCaqqu5bpsWIdyzGvvRrBW7rdDo2I6ZSKeq7B8x0XV/bwJWAJEnHSMwBDUEQWq5GfsJthUJhlVuv11uckyiGgiH2RWK73RYRb2cymbG7gnK5vIX9USwWI1yAI/KjLGK7teEI8HN1TizrnZWdRxxsNps8vI3YLpVKbB2EWB6XkMHzgAlvriYRSW+app1Mpy/jSCRSRSyDUON5nuJGytaAHI/vVPv9p/FischivL96gEP2bGxorhVFqYXDYQFCScwBYa9EKU1OlAkB+QLEU2AGaJ7PWKlUDiF2BBw4P9Mt/KUoije+5uAv9gGcjD6Kg4wu3AAAAABJRU5ErkJggg%3D%3D" /></a>';}ls("",function (){$=jQuery;$("a[name]").each(function (i){$(a(,"NAME")).insertBefore(this);});$("[id]:not(input[type='hidden'])").each(function (i){switch(this.nodeName){case'TD':$(this).prepend(a(,"ID"));break;default:$(a(,"ID")).insertBefore(this);}});});})()

  5. One more CSS fix - the z-index should be set so something high.

  6. Thanks for the helpful tool! Here's an update on the patch by Niels Ulrik Andersen, incorporating the z-index, too. Additionally, I've cleared any margin and padding (potentially inherited from parent elements), and switched the URL scheme for jQuery to // to avoid a "Blocked loading mixed active content" error on HTTPS pages.