Quick Tip: Dynamically add an icon for external links
read 33 commentsA common feature I've seen on “web 2.0” sites and wikis is the "external link" icon:
. While I'm not crazy about the idea of sticking these little images all over the HTML, they're a great candidate for using progressive enhancement. In our case, we can use jQuery to add the images pretty easily.
Test the hostname
To identify the external links, we test for the location.hostname against the link's hostname, which will be represented by this.hostname once we have the selector in place, and make sure the two don't match. We should also test for the mere existence of this.hostname to avoid problems or false positives with "mailto" links. our tests will look like this: this.hostname && location.hostname !== this.hostname.
Use the filter function
Now let's wrap that test in a filter function. For our example, we'll test all links inside an "extlinks" element that match the above test. Here is what it looks like:
A $(document).ready() is wrapped around the script so that it will execute when the DOM has loaded. Line 4 shows the insertion of the image after each of the external links.
Demo
Here is a little demo using the above code. Of the three links, only the second points to a different site. We should see the external-link icon next to it.
So, that's it. Short and sweet.
33 comments
2 Pings
-
[...] Good tip from — >http://www.learningjquery.com/2008/08/quick-tip-dynamically-add-an-icon-for-external-links#more-103 [...]
-
[...] to know how to add fancy “this is an external link” icons to his page. Since that was basically written for me by Karl Swedberg at Learning jQuery, I decided to take it up a notch (using some AJAX and JSON [...]












Personally I prefer doing this with pure CSS (using background images and padding with a special class, which is what MediaWiki [for example] does), but JavaScript is another way to go, I suppose.
@Voyagerfan, using css is great if all browsers could support a[href^=http].
Is this also possible for set the favicon on the externel link?
If you wanted to implement a CSS version then this is simply a case of doing the following:
And the CSS would go something like:
@Voyagerfan5761: the only way to do this consistently across browsers without using JavaScript is to add a class to the link manually or write some sort of server-side script that will do it for you. Doing it manually isn't always practicable, especially for content-managed sites. But if you have total control of the site's HTML, and you're vigilant about adding those classes, then pure CSS is probably the way to go.
@James Chan: in a content-managed situation, the
a[href^=http], while certainly a good start, isn't completely reliable. What's to stop someone from adding the fully qualified URL to a same-site link? If the attribute selector were bulletproof, it would certainly be preferable to the filter function I recommend in the post.@Ben: yep, that's another way to do it for sure. Not "pure CSS" as Voyagerfan5761 was suggesting, but definitely a nice option. If your links have text decoration, the underline will extend underneath the icon, so it might not be a viable option, depending on the look you're trying to achieve.
@Frank: sure, it's possible. Google is your friend here. First result for "external link" favicon is for a jQuery external link favicon plugin.
I played around with this task a while back and came up with an exhaustive, js-based solution which accounts for accessibility and additional usability, with a built in new window toggler.
More recently, I added a jQuery-based version.
So, fwiw…
Flag & toggle external links with jQuery
It hasn't been touched for quite a while, so may not be taking advantage of recent improvements in the jQuery library.
There's also the straight css way. Doesn't work in old browsers:
a[rel="external"]{
background:url(images/external.png) top right no-repeat;
padding: 12px 0 0 0;
}
Of course that means you'd have to add the rel to your links. But I'm a fan of rel, and you could add the rel with jquery if you wish.
In a perfect world.
I like the adding a class part you mention in comment #4. Gives me a lot more control. It does not work in IE6/7 though for me.
Karl,
Will calling after('') for each external link result in a new image download for each link? I could see this being an expensive process for a site with 10-15 external links on it, or even more for that matter. I wonder if generating that image and storing it in a var outside of the filter call would be a speedier solution?
Thanks for the article. Pax,
- Stan
@Karl Swedberg: I use the CSS suggested by Ben for links that have :hover text-decoration set, and the underline doesn't go past the end of the text. Is the behavior of that different in browsers like IE7 (which I don't have)?
@Jethro Larson: The rel method works, but if you're adding something using JavaScript it won't show up to robots, so really you're using the same method as I do (and what Ben suggested), just with a different attribute of the <a> tag.
@Bill Posters: Looks like an interesting idea there. I'll have to remember that in case I ever want or need that behavior for a future site. Thanks!
@Voyagerfan5761: hmm. Could be that my recollection was faulty. Regardless, if you're using JavaScript to display the icon, does it really matter if you're using a CSS background image or inserting the image into the DOM? As I already said, there isn't a pure CSS way (that I know of) to show the icons without adding a class manually or having some server-side script take care of it—not cross-browser anyway.
Seems to me that for accessibility reasons, it might actually be preferable to insert the image rather than use a background image, because then screen readers can read something like "(external)" from the
altattribute.Of course, I could be wrong about all this. Or, it could be just a matter of taste. I'm interested in hearing your reasons for preferring the CSS background image method.
@Karl Swedberg: I tend to prefer doing things without JavaScript whenever possible. If I can make something work in a browser whose user has disabled JS, why do it in a way that would deny that user the experience? People do browse without JS (though I can't understand why ;-), and I want them to get as much of the experience as possible.
With regards to accessibility: At least on the main site I work on, having a screen reader read out "(external)" isn't important, because the site uses external links that are clearly external from the context.
I think it's a matter of personal preference and site-specific reasoning. Your preferred method of doing things would make perfect sense to me if the site involved was different. For example, I could see how it could be a good thing to do here on Learning jQuery because it would provide a practical example of jQuery code.
@Voyagerfan5761: Yes, I prefer doing things without JavaScript whenever possible, too. But, as I've noted more than once now, it's not possible to do this with pure CSS in a way that IE6 will understand, unless you manually add a class (or perhaps use some server-side logic).
You say that you use the CSS suggested by Ben. That's great. But Ben is applying that class with JavaScript.
It should be pretty obvious from my posts here as well as from my books and my suggestions on the jQuery discussion list that I don't advocate using JavaScript for presentation when a cross-browser, pure-CSS solution is available.
Still, do you agree that there are times when pure CSS is insufficient for this task? If not, show me the pure CSS selector that will add a background image to all external links on a page — keeping in mind that many sites are content-managed by less-technical users who won't be adding a class manually.
@Karl Swedberg: Yes, there are times when pure CSS is insufficient for a lot of things. This is only one of them. I'm just saying that I haven't found any situations in which my method has proven to be inadequate. Not to say that such situations don't exist.
Re your views being obvious, I honestly haven't been following the comments here much, nor do I regularly read the jQuery mailing list. I'm also sorry to say that I haven't bought any Web development books in over a year, and the last ones were on the subject of PHP. So I didn't know of your similar preferences. But I do now.
great tip! muy bien!
I want to not display the external image if the link or tag contian the tag of image (img)
how do you do for make it?
sorry for my bad english...
Hi Mte90, you just need to make the selector more specific. So, instead of, for example,
$('#extlinks a'), you could do something like,$('#extlinks a:not(:has(img))'). Hope that helps.Thanks!
It's perfect!
Hey! This is basically awesome, thanks. One thing I'm wondering about, though, is how easy it would be to add target="_blank" inside the <a> tag as well as the image after it.
I know very little JavaScript, and even less jQuery, but it seems to me that if there is a .after possibility, there must be some sort of .inside equivalent. Trouble is, "after" isn't a word you can just search for in Google. Does anybody here know how to make this happen?
Cheers!
Matt
Hi Matt!
Use the
.attr()method:.after( ... ).attr('target', '_blank');One other thing, Matt -- even though I'm not crazy about the idea of forcing a new window to appear, you can certainly do it without the
target="_blank". Something like this should work:Thanks a lot, Karl! Yeah, I know I know, user-customizability, etc. The website I work on is not my own though, and I have to do what I'm told. We're in the lengthy process of manually adding
target="_blank"to every external link, so this solution is a lot better, thank you!Hey Matt! Glad the solution is going to work for you. I definitely understand the bind you are in with external constraints determining what you do with the site. I just felt the need to put in that note about forcing a new window to avoid the wrath of others who might decry such an outrage.
Karl - great tip!
I'm using this on a web site I've built successfully using your method. One thing- how would you make it so the icon is part of the link so when a user hovers over the icon or the link they are both clickable? Just like is done on Wikipedia and MediaWiki? Thanks in advance.
Hi James,
You would do it the same way, except instead of using
.after(), you would use.append():That's great Karl, couldn't have been simpler. Thanks for that simple tip. However, is there a way to make it so the icon is not underlined. I should have stated better how I have my links setup. text-decoration is underline by default and on hover text-decoration is none. Was kinda hoping I could not have the icons underlined at all. I want them just hanging out there as an afterthought, no decoration.
Hi James,
I suppose the best way, in that case, is to simply set a class on the link and then use CSS to position the icon as a background image. So, for the jQuery:
And for the CSS, something like this should do:
Karl,
Great tip, but I found an issue with non-standard ports and Safari (mac)...
I am using mampstack (bitnami.org) for my development server, and it runs apache on port 8080, so I connect to my page using http://localhost:8080.
Using this as my test HTML:
<p><a href="http://www.yahoo.com">Yahoo</a></p>
<p><a href="/test">Test</a></p>
The first link should have class="external" added, but in Safari (mac) I found that both links were getting the class added. With a few alert statements, I found that in Safari (mac)
this.hostnamereturns localhost:8080 andlocation.hostnamereturns localhost.Obviously, this makes the script flag the second link as external when it shouldn't.
Note, this is only an issue in Safari (mac) (FF and IE are fine) and ONLY if you specify a port in the URL
To fix it, I made this change:
return this.hostname && this.hostname !== location.hostname;becomes:
return this.hostname && (this.hostname).split(":")[0] !== (location.hostname).split(":")[0];This way the script only looks at the hostname and discards the port.
Just thought I'd share this in case anyone else had the same problem, and see if anyone has a different approach.
Josh,
Thanks a lot for that tip. Looks like another edge case I'll have to take into account.
Okay, so my question would be: How do you do this, but apply it to only text links. In other words, make it ignore links that might be wrapped around images.
Hi Michael,
You can specify that in the selector. Something like this should work:
Thanks, that seems to have gotten me partway there, but it doesn't seem to be ignoring images quite right. I have:
Any thoughts on what I'm doing wrong here?
Nevermind, I got it!, it was:
$(this).not(":has(img)").addClass('psuExternalLink');