You know that feeling at the grocery store express lane when you find out that the person in front of you actually has 74 items, a book of coupons, a checkbook and arthritis? That’s the feeling I get after clicking on an innocent looking link that goes to a PDF unexpectedly.

HTML Form Builder

Inspired by the TargetAlert Firefox extension that “provides visual cues for the destinations of hyperlinks” and Christian Heilmann’s Image previews with DOM JavaScript, we wrote a simple set of unobtrusive JavaScript functions to find links on a page that go to amazon product pages, pdfs, word documents or whatever destination that might slow down the browsing process and adds an icon next to them to let you know what you might expect to find behind a link. Just add the JavaScript include to your header and nobody leaves your site feeling violated.

See it in Action

The Files

How it Works

We start out by adding the onLoad event listener to call ‘linkPreview()’. ‘linkPreview()’ traverses the dom looking for non-image links—this is to keep the script from adding icons to your images. If the url goes to an domain, a span with a little book is appended to the link node using the ‘append()’ function.

addEvent(window, 'load', linkPreview);
function linkPreview(){
    var links = document.getElementsByTagName("a");    for (i=0; i<links.length; i++){
        var currentLink = links[i];
        var images = currentLink.getElementsByTagName("img");
        // Check if the link is an image. We don't want icons next to images.
        if (images.length == 0){
            var linkHref = currentLink.href;
            // Find all links directed to 
            if (linkHref.match(/{
                append(currentLink, "amazon");
                checkLinks(linkHref, currentLink)

If the link is not to, we move on toward the ‘checkLinks()’ function which splits the href and looks for a doc, pdf, xls, or whatever extension of your choice.

function checkLinks(linkHref, currentLink){
    var linkHrefParts = linkHref.split(".");
    // extension is the last element in the LinkSplit array
    var extension = linkHrefParts[linkHrefParts.length - 1];
    // In some browsers there is a "/" placed after the link. removes the "/"
    extension = extension.replace("/","");
    if( extension in { doc:1, pdf:1, ppt:1, txt:1, xls:1, zip:1 } ){
        append(currentLink, extension );

If the function finds a match, we call the ‘append()’ function add the proper ‘span’ elements after the link.

function append(currentLink, extension){
    var span = document.createElement('span');
    span.innerHTML = "&nbsp;";
    span.className = extension;

Note there is an ‘&nbsp’ inserted into the span because there are some CSS issues with an empty class in Internet Explorer. The great thing about this code is that it separates the presenation from the behavior. In your CSS file you can style the presentation to your heart’s content. On Particletree, we took the TargetAlert Extension approach and added icons to the links. Here’s a sample of what our CSS looks like:

padding:0 20px 0 0;
background: url(doc.png) no-repeat right;
padding:0 20px 0 0;
background: url(ppt.png) no-repeat right;

And that’s it. We think it’s really going to be useful for our readers on Particletree and we hope it’ll be versatile enough for you developers out there to handle whatever you might want to throw at it. Obviously, let us know if you see anything we can improve upon.

HTML Form Builder
Chris Campbell

Preview Your Links with Unobtrusive JavaScript by Chris Campbell

This entry was posted 5 years ago and was filed under Features.
Comments are currently closed.


  1. Dan Mall · 5 years ago

    Great post Chris. Now, it could just be my experience (or lack thereof) with Javascript, but is there a reason that you’re using innerHTML as opposed to createTextNode?

  2. Chris Campbell · 5 years ago

    Thanks Dan. I wish I could give you a better answer but the honest truth is I finished this about late last night and knew that would work.

    While on the topic, quirksmode has a nice post about the W3C DOM vs. innerHTML.

  3. Oscar · 5 years ago

    Nice, it would be a cool plugin for WordPress, Movable Type, etc..

  4. Mr. Anon · 5 years ago

    Nice stuff, but it’s not quite unobtrusive yet. You should move the window.onload to an event listener like Simon Willison’s addEvent()

  5. Relapse · 5 years ago

    That’s very nice right there. As a question, is it possible to use a style standard (e.g. icon_append_doc, icon_append_pdf) so that the css can be extended/ contracted to control the link images that are shown and the Javascript can parse the page’s styles to determine which icons it can show?

    Just pondering an extra level of abstraction.

  6. Chris Campbell · 5 years ago

    Mr. Anon,

    You’re right and if you have more than one script it should not be done that way. When I get a little time I’ll update it. For those interested in the subject, a great tutorial can be found at

    Relapse, Thanks for the positive feedback. As to your question, do you mean add a class to the link?

  7. Relapse · 5 years ago

    Chris, Not quite. I was wondering if your javascript could read the class styles provided to the page, and populate the switch cases in checkLinks based on available classes. e.g., the css includes the classes linkPreview_doc and linkPreview_pdf. The checkLinks script looks at the page’s styles and finds those two classes with the linkPreview_ prefix; it now knows those are link preview styles it can add to links on the page that have a ‘doc’ or ‘pdf’ suffix. Of course, your amazon-style links would be trickier (or just require a different prefix to the class)

  8. l.m.orchard · 5 years ago

    By the way, do you know about Behaviour? It’s a little JS lib that lets you trigger functions on elements matching CSS selectors on page load. For example, you could have something like: var rules = { “a[href$=’.doc’]”: function(e) { append(e, ‘doc’) }, “a[href$=’.txt’]”: function(e) { append(e, ‘txt’) }, “a[href$=’.zip’]”: function(e) { append(e, ‘zip’) }, “a[href~=’amazon’]”: function(e) { append(e, ‘amazon’) }, // … }; Behaviour.register(rules);

    I haven’t tried this, but I think I’ve got the selectors right. Of course, all the code behind Behaviour adds some overhead to the page, but it’s certainly succinct.

  9. l.m.orchard · 5 years ago

    (Oh, and nice work by the way! :) I didn’t mean to sound dismissive or anything, with the Behaviour mention.)

  10. l.m.orchard · 5 years ago

    Also, since my links were eaten, Behaviour is here:

  11. Chris Campbell · 5 years ago

    Relapse, I think I understand what you’re saying and don’t see why that wouldn’t be another option.

    I.m. orchard, Thanks for the tip and I’ll defiantly check that out.

  12. anon1234 · 5 years ago

    you can also do this with css3 selectors:

    a[href*=”.pdf”] { padding:0 20px 0 0; background:url(pdf.png) no-repeat right; }

    of course this doesn’t work in ie…

  13. paul haine · 5 years ago

    I’m seeing two icons instead of one when in Firefox…am I the only one?

  14. Kit · 5 years ago

    This is very cool but it will only be useful to those who can see where as people who can benefit from something like this the most would be those that are visually impaired and have to use a screen reader to browse content. Something like this would be the only indication they have before selecting a link of where it would go. I wonder if you can’t have it add text also that is hidden from the browser but gets read by screen readers.

  15. John Keane · 5 years ago

    Cool, thanks very much.

    N.B. The .txt class styling is missing from the css file in

  16. Chris Campbell · 5 years ago


    Behavior is definately something I’ll keep in mind for future projects as it can clean up code. For the purposes of this script, I’ll leave it out and let the users decide if the overhead is worth it.

  17. Michael Geary · 5 years ago

    Great idea, bad code.

    You’ve gotten several good suggestions for different ways to approach the problem, but I just want to talk about the details of the JavaScript code itself. You have an extremely good idea here, but the code needs some improvement.

    • Your brace and indent style is all over the map. Pick one style and stick with it.

    • Inconsistent use of semicolon. Use it or don’t use it, don’t mix and match. Most JavaScript experts recommend using the semicolon always.

    • Bad variable names. Examples:

      “hasImg” is a good name for a boolean. It’s a bad name for an array.

      In the checkLinks() function, the second parameter is named “links”, but it is not a collection or array. It is a single link.

      Shouldn’t “replaceString” be called “extension” or “ext”? It would make more sense.

      “theNewSpan” would be better called just “span”. “the” and “new” add no information. I know it’s “the” something, and I know it’s “new” because it’s created with createElement().

    • Most of your variables are GLOBAL. You need to “var” all your local variables.

    • linkHref.match(/ does not do what you expect. The ”.” matches any character, so this regular expression will match a string such as “amazonscom”. Unlikely to be a problem in this particular code, since you’d be dealing with your own page, but it’s better to get this right and use ”.” instead of ”.”.

    • You use links[i] over and over again. It would be better to do:

      var link = links[i];

    and then use link instead of links[i].

    • You comment the obvious and fail to comment the subtle. For example:

      // find the links href string linkHref = links[i].href;

    That comment doesn’t tell me anything I don’t know from reading the line of code below it. It is better to omit comments like this which provide no information. But there is no comment explaining this code, where the purpose is not obvious:

    replaceString = replaceString.replace(”/”,””);

    Actually, that sequence of three lines setting up individualLink and replaceString just looks odd. There must be some cleaner way to do this, but I didn’t look at it more closely.

    • The switch statement in checkLinks() is a real jaw-dropper. You should hear alarm bells in your mind when you see that kind of repetition. The entire switch statement could be cut down to:

      if( extension in { doc:1, pdf:1, ppt:1, txt:1, xls:1, zip:1 } ) append( link, extension );

    (The “1” values are not significant here; they are just placeholders.)

    The switch statement does make it easy to map more than one extension to the same CSS style, which my code above doesn’t. But that’s easy too. Suppose you needed to have “fdf” be treated the same as “pdf”. You could easily do it this way:

    var extensions = { doc:0, fdf:’pdf’, pdf:0, ppt:0, txt:0, xls:0, zip:0 } if( extension in extensions ) append( link, extensions[extension] || extension );

    (Also, I used “extension” instead of “replaceString” as mentioned earlier.)

    • A nitpick, but the parentheses in these two statements serve no purpose and create a speed bump when reading the code. Leave them out for better clarity:

      theNewSpan.innerHTML = (“&nbsp”); theNewSpan.className = (type);

    Sorry if I sound critical; that’s not my purpose at all, I just want to help you improve your code. You have a popular site with a lot of good ideas. People will look to your JavaScript as examples for their own code, so it is worth getting the code right.

  18. Chris Campbell · 5 years ago

    I truly appreciate the breakdown of the code Michael and am flattered a developer of Acrobat took the time to throw a few pointers my way. Like a lot of developers, when confronted with a task I sometimes “just want it to work” rather than take my time to do it the “right” way.

    You’re also right that with the sites growing popularity more effort and care should be given to the code since readers will copy/paste into their own scripts. I will take your comment to heart and adjust this and future scripts rather than just posting a working version.

  19. Patrick Haney · 5 years ago

    I saw something similar to this a while back, but I believe it used CSS and didn’t work properly in anything but Internet Explorer. It’s good to see a Javascript solution that works on more browsers (though I did notice some of the icons had background color in IE).

    This is a good step in the right direction, but it could use a bit of tweaking, as others have mentioned. So far, so good.

  20. Chris Laprun · 5 years ago

    I’m with anon1234. I prefer a pure CSS solution, which doesn’t work in IE (but what does?):

    a[href$=”.doc”]:after { margin-left: 5px; padding-left: 1px; content: url(doc.png); }

  21. daniel poynter · 5 years ago

    Why depend on web developers… = ) Can we say greasemonkey?

  22. Chris Campbell · 5 years ago

    Michael, Made most of the changes you suggested. Again, appreciate the feedback. The script now also loads through addEvent().

  23. Hob Gadling · 5 years ago

    Look at “this”: post.

    Relevant info is:

    content: “p”; color: red; background-color: yellow;

    Put that in your userContent.css and you’ll get notification only on hovering over the link.

  24. Michael Geary · 5 years ago

    Hey Chris, the code looks great now. I’m really glad you didn’t take offense at my comments. Reading them over again, I can see I wasn’t as polite about it as I should have been! (That’s what I get for posting comments late at night while traveling..)

    BTW, I’m a former developer of Acrobat. Not working for Adobe any more. But that’s a whole ‘nother story I’ll talk about some other time. :-)

    It looks like one little thing went wrong in posting the new version of the code. The line that sets span.innerHTML to a non-breaking space is displaying as ” ” instead of the ampersand-nbsp-semicolon. I recall this displayed OK in the first version of the code. Probably just need to escape the ampersand.

    Keep up the great work! I’ve been getting some really good tips from your site and look forward to what you have in store.


  25. Jake Tracey · 5 years ago

    Wow, awesome idea. Excellent to see you guys coming up with good ways to do cool things like this, keep em coming!

  26. beth · 5 years ago

    i never knew about that firefox extension, this is a godsend! i hate accidentally opening a pdf and having my browser crash. i’ll be certain to include this javascript on my own sites, thanks!

  27. Stuart Robertson · 5 years ago

    This is a very handy bit of code — especially for large sites where staff add links to all sorts of documents. Thanks!

  28. qwerty · 5 years ago

    This is cool. Thanks.

    One question, would it be possible to modify this so that by default, it’s off? Then, if the user wants it, they can use a radio box to turn it on (with a cookie).

    Thanks again for making this.

  29. Chris Campbell · 5 years ago

    Yes you could do that qwerty. Like you said, just set a cookie and check if the user wants the script on or off.

  30. Mark Priestap · 5 years ago


  31. Arthi Amaran · 5 years ago


    I’m going to include this in my website (of course with due credits and acknowledgements).


    Keep the good work going!

  32. Paul · 5 years ago

    Why is this so exciting exactly? Just seems like a load of overhead on the client for something that can be achieved by a developer not being lazy and adding the icons beside the links…or am i missing something?

  33. Ryan Campbell · 5 years ago

    I don’t know that I would classify it as being lazy. In a publishing environment where non developers have the power to create links, they shouldn’t be expected to place images next to them as well.

    I see your point though. This could easily be converted to run server side as well by a plugin for a CMS.

  34. Kyle Haskins · 5 years ago

    I made it a standard to use small PDF icons on all my sites, but about 2 weeks ago a client refused and made me take them all off. The client refered me to Adobe’s trademark guidelines on the PDF icon which, if followed exactly, limited you to using their 32 x 32 pixel image. I e-mailed Adobe to ask them about this, never got a relpy, but to my surprise they have updated this page and added a small file icon. I think the client over reacted, but now I’m curious how serious Web designers should take these type of guidelines?

  35. Kyle Haskins · 5 years ago

    I also have a couple comments on the actual icons that you are using/distributing.

    1) Your icons for Word, Excel, and Powerpoint are not actually file type icons, they are the program icons. You should probably stay consistant and use file type icons similar to the ones you have for PDF and TXT.

    2) Your ZIP icon has the same problems I just mentioned, and is also taken from the Windows XP operating system. If we are developing standards, we should keep in mind the copyright issues with using icons on our sites.

  36. Chris Campbell · 5 years ago

    Thanks for the comments Kyle.

    Concerning the icon looks, we actually tried to mimic the TargetAlert firefox plugin.

    Here’s what I was able to find about the Microsoft icons,

    “Microsoft product icons are the thumbnail-size images indicating that a Microsoft product has been installed on your operating system. Use of our icons is permissible in training manuals or documentation written for and/or about a Microsoft product. In such cases, use of Microsoft icons must be specific to the function of the icon within the Microsoft software and not used as a graphical element or design for your own purposes. Further, icons cannot be modified or altered and must appear as they would within the Microsoft software.”

    As for Adobe, the permissions can be found at Like you said, there is a large and small version available for download.

  37. Thomas Tallyce · 5 years ago

    Is there some way of making the image appear at the start of the text of the link rather than after it? In a list of documents, this appears much neater.

    Presumably this is a matter of altering the line


    but I’m not a DOM expert so not clear exactly what to change.

  38. Rick Owens · 5 years ago

    Nice! I’ve tweaked the append function to directly insert an image rather than using a css-provided backgroud, mainly so that I could quickly add alternate text and a title (in case image loading is turned off).

    function append(currentLink, extension){ var span = document.createElement(‘span’); span.innerHTML = “”; currentLink.parentNode.insertBefore(span,currentLink.nextSibling); // span.className = “file-” + extension; }

    In my style sheets I hide or scale img.extension as needed; right now the file-{extension} classes are there for fine control in the future.

    Thank you!

  39. Rick Owens · 5 years ago

    Hmm, let’s try that code again:

    function append(currentLink, extension){ var span = document.createElement(‘span’); span.innerHTML = “”; currentLink.parentNode.insertBefore(span,currentLink.nextSibling); // span.className = “file-” + extension; }

  40. Chris Campbell · 5 years ago

    Very cool Rick. Thanks for adding that.

  41. Rick Owens · 5 years ago

    Since the code won’t post, here’s a link to the modified version I’m using:

  42. Stephen Bau · 5 years ago

    I notice is powered by Textpattern. Any chance someone might be able to adapt this feature as a Textpattern plugin? I can imagine a plugin that determines the file type from the download file name variable and modifies the anchor class based on the file type.

    BTW: Nice live comment preview feature. Is this something available as a Textpattern plugin?

  43. Mark Priestap · 5 years ago

    Thanks for this handy tool. What is the benefit of using PNGs over GIFs, apart from transparency? Just wondering…

  44. Kevin Hale · 5 years ago

    Mark there are a lot of sites dedicated to answering that. Here’s some to get you started:

  45. Christian Ready · 5 years ago

    Hi Chris,I really like your code a lot and I have already started to use it. I did try to do what I thought would be a simple augmentation - namley adding icons for audio files such as .ram (real), .wmv (windows media), .mov (quicktime), and .mp3 (mp3). I created the pngs and put them in the same images directory. I also edited the line of code in the javascript file by adding:

    mp3:1, ram:1, mov:1, wmv:1

    However, the icons to not show up when I reference any one of those files. Do you know what I might be doing wrong?

    Thanks again!

  46. Ryan Campbell · 5 years ago

    Hey there - did you add the CSS classes for your new file types as well?

  47. Christian Ready · 5 years ago


    Thanks, Ryan :)

  48. Marcus Tucker · 5 years ago

    Great script, though not sure I’ll ever use it - it’s so rare that I link to a non-HTML document.

    However, I might adapt it for indicating whether a given link opens in a new window or not… ;)

  49. eric · 5 years ago

    Good idea mark. I’m implementing this into our site. Thanks Chris.

  50. JustAnotherCommenter · 5 years ago

    Nice idea! Using TargetAlert, I realized that it would be nice if this was built into many sites, although the TargetAlert occasionally skews a page layout. Now you’ve done it!

    It would also be nice to have an ebay icon/link style similar to the amazon for sites that link to ebay stores etc.

  51. RaDDiX · 5 years ago

    lay-out bug [background is missing a top px and 3 bottom px’s] if the span is displayed in eg a paragraph, can be solved with padding:1px 20px 3px 0;

  52. Thomas Tallyce · 5 years ago

    The new version at is an improvement on an already great implementation.

    Does anyone know how to make the icon appear before rather than after the link text?

  53. Mark Galeassi · 5 years ago

    Thomas, I havent used this code yet but I just took a look at it. It looks like you would need to change the padding in the css file to pad on the opposite side and tell the background to go to the left side and not the right. Hope that helps

  54. Robert Nyman · 5 years ago


    I know I’m late to the party here,but just wanted to add my two cents. Great idea, I like the concept.

    My only suggestion is to avoid declaring the variables within the loop, but to instead do it before (for performance reasons and better code). Like this:

    var currentLink; var images; var linkHref; for (i=0; i

  55. Robert Nyman · 5 years ago


    I know I’m late to the party here,but just wanted to add my two cents. Great idea, I like the concept.

    My only suggestion is to avoid declaring the variables within the loop, but to instead do it before (for performance reasons and better code). Like this:

    var currentLink; var images; var linkHref; for (i=0; i<links.length; i++){ currentLink = links[i]; images = currentLink.getElementsByTagName("img");

    // Check if the link is an image. We don't want icons next to images.
    if (images.length == 0){
        linkHref = currentLink.href;    // Find all links directed to 
        if (linkHref.match(/{
            append(currentLink, "amazon");
            checkLinks(linkHref, currentLink)


  56. Chris Campbell · 5 years ago

    Good call, Robert. Thanks for the heads up.

  57. theUKdude · 5 years ago

    I would very much like to know how to amend the script slightly so that icons are added to the front of the links instead of the end…

  58. theUKdude · 5 years ago

    btw Christian, just ensure that the array of file extensions are listed alphabetically and they will then display your additional icons

  59. theUKdude · 5 years ago

    OK, figured it out. To display the icons before the links amend the line in the append function to read as


    Note the removal of ‘nextSibling’ and remember to alter the padding in the css.

    Thanks to Tim the Site Doctor for the answer.

  60. Thomas Tallyce · 4 years ago

    Thanks, theUKdude, that’s a great help. Looks much better now when the icons are at the front, especially when there are more than one in a list.

  61. provig · 4 years ago

    Performance Anxiety: Anxiety about sexual performance can negatively affect erectile function. dysfunction erectile medical

  62. nick · 4 years ago

    hi…good site.

  63. nick · 4 years ago

    hi…good site.

  64. aruhowqov · 4 years ago

    very young teens in bikinis very young teens in bikinis very young teens in bikinis

  65. qhuhagji · 3 years ago

    Still can’t trust you i’m finished, who is, jessica alba xxx besides, he thought. Still, just.

  66. getivesxu · 3 years ago

    Her arsehole and i just as fiercely squirt mpg as he was a.