psst.. this blog is on hiatus.

Hyperlinking best practices, pt. 2: Distinguishing external links with CSS3 and regular expressions

In part 1 of this three-part “Hyperlinking best practices” blogging miniseries, I talked about the most effective way to visually present hyperlinks. Now, in part two, I’ll describe how to visually convey to users whether a link is external to a website (that is, it is not located on the same domain) or internal (part of the same site).

Background: Why does it matter whether a link is “external” or “internal?”

If you’re on a website and you’re reading stuff on it, you probably want to be there. You might want to know that by clicking a link, you’d leave that site and visit another website, where the quality of the content would be unknown. Perhaps you’d be more willing to follow a internal link, thinking, “I like this site!” and less willing to follow an external link, thinking, “That’s probably a porn site with popups!” This is the premise behind distinguishing external, offsite links from internal links. Supplying users with this contextual information helps them make decisions about whether it’s worthwhile to click a link. One way to convey this information visually is with CSS3’s attribute selectors.

CSS gets really fancy with attribute selectors

Attribute selectors allow you to apply styles to HTML elements with attributes that meet specific criteria. For example, you might style all P elements with an ONCLICK attribute green. CSS3 attribute selectors go above and beyond the simple matching of IDs or classes; in fact, very rudimentary support for regular expressions is included in some browsers (Mozilla and Safari), allowing to really get your schwerve on and do something useful.

Let’s take this website as an example. When I use the A (anchor) tag to hyperlink to other content on my site, the “href” attribute can take several forms:

Absolute link: underscorebleach.net
<a href=”http://underscorebleach.net”>
Absolute link: www.underscorebleach.net
<a href=”http://www.underscorebleach.net”>
Root-relative link
<a href=”/content/jotsheet/”>
Relative link
<a href=”../../content/writing/”>

Even though the last two links styles are written in the raw HTML in relative style, they are available to the browser as an absolutely qualified URL. Both of the relative-style links above will be translated into their absolute forms by the browser upon page load.

Off-site, external links will always take the following form:

Absolute link: www.website.com
<a href=”http://www.website.com”>

CSS’s take on regular expressions: Substring matching attribute selectors

Using “substring matching attribute selectors” (boy, the W3C has a way with words), we can differentiate the first group of links above (internal links) from the second (external links). The attribute we’re interested in takes the form element[attribute^=value], which for those of you familiar with regular expressions, roughly equates to “match a tag with an attribute that begins with the following string.”

Here’s the CSS for my site. The first rule matches external links, while the second matches links on my site. For simplicity’s sake, we’ll start with selectors only and no properties or values. In the next section, we’ll fill in the gaps.

a[href^="http"]:after {
	}

a[href^="http://underscorebleach.net"]:after,
a[href^="http://www.underscorebleach.net"]:after {
	}

The regular expression support in CSS3 is very rudimentary, as I mentioned, so you can’t consolidate the above two rules into something like a[href^="http://(www\.)?underscorebleach.net"]:after. You need to be very explicit. The first regex applies a style to link starting with “http.” This is a bit too broad, though, and the second rule applies styles to internal links that start also start with “http” but more specifically point to an underscorebleach.net domain.

I should mention that the above rules could theoretically be more efficiently achieved via the :not negation pseudo-class, but I ran into some inexplicable and unsolvable problems with :not: when I tried to put it into action with Mozilla. I never figured out what the deal was, but the above rules are pretty simple and straightforward and there’s no need to get too fancy-pants.

And now, the styles

Did you notice that there are no styles in the CSS above? I hope so, Sherlock. That was on purpose. Now we need to define some useful properties and values to pair with the above selectors in order to distinguish internal links from external links.

We could make external links a different color, or change some border property or something, but it’s clearer to visually indicate with a symbol that a link points to an external site. It’s become convention to do with this with an arrow, and here again we need CSS3 to insert an arrow.

We’ll use the :after pseudo-class combined with the content property to write (via CSS) an arrow next to an external link. The content When you think about it, this is really slick. Via a stylesheet, you’re changing content. That’s a powerful concept (and probably explains the limited browser support). The unfortunate thing is that it’s supported only in Mozilla and Safari. There are cross-browser approaches, but I don’t recommend them, and I explain why below.

The last ingredient in the external links with CSS3 is unicode. That’s the character set with everything under the sun, and we use it in CSS3 to insert characters, such as the arrow here. This topic is covered nicely by MinutiaeMan, so I’m not going to cover here. With all of these ingredients, we mix it up, throw it in a stylesheet, and voila!

a[href^="http"]:after {
	color: #ACAC65;
	content: "\21D7";
	font-size: 10px;
	}

a[href^="http://underscorebleach.net"]:after,
a[href^="http://www.underscorebleach.net"]:after {
	content: "";
	}

For local links, the more specific second CSS rule “cancels out” the first rule and the arrow is not added to internal links.

Caveats

As I mentioned above, CSS3 attribute selectors are currently supported only in Safari and Mozilla. That’s a small chunk of the Web audience, but it’s a big chunk of the clued-in Web audience that will understand what that little arrow means, so it’s not a total wash.

Also, the W3C’s validator chokes on attribute selectors. Your stylesheet won’t validate, and you’ll get an error like:

Combinator ^= between selectors is not allowed in this profile or version

There’s no way around it. If you’re silly and you’re irrationally obsessed with validating, avoid this technique.

Other approaches—and why I ultimately don’t like them

You can go about this whole endeavor a couple of other ways. First, you can stay on the CSS train and instead of using the content property, you can use a background image. That way, you can have a fancier image, and as an added bonus, you don’t have look at 50 PDFs at Unicode’s website. If you’re not careful, the background image can wrap around in certain situations and look funky. Other than that, I don’t know that there’s any advantage to using content over background-image other than that it’s fancy, CSS3 property that will impress your friends and score you chicks.

The truly different approach to designating offsite links is to use Javascript. This is a cross-browser solution, of course. Basically, you set up a simple function called onLoad to change all links with an href matching given criteria to a CSS class. Then, in your stylesheet, you give links of this class a background-image, or you do the same content arrow thang described above.

What’s the problem with this? Speed. The Javascript function is not called until page load, so the browser has to trawl through the links and change the class of every link on the page matching the regular expression in the function. Then, the CSS must be applied. The entire process can be visibly slow, and on a large page (e.g. a long blog archive page), it degrades the user experience and make the user wonder what the heck is going on.

Other reading

4 Responses to “Hyperlinking best practices, pt. 2: Distinguishing external links with CSS3 and regular expressions”

  1. 1
    Douglas Clifton Says:

    By passing the query string variable:

    profile=css3

    to W3C’s Jigsaw you can indeed use these attribute selectors and still have your pages validate. See my site for examples.

    Thanks Tom,
    -doug

  2. 2
    tom sherman Says:

    Thanks, Doug. For everyone else, it’s also possible to pass other parameters to the W3C CSS validator. You can see a list of them by checking out the advanced options at Jigsaw, selecting some options, and looking at the URL.

  3. 3
    Ton Vink Says:

    You can use the content property and still use your own pictures by using:

    a[href^="http"]:after {content:url(”picture.jpg”);}

    Real cool, this after and before stuff. Any chance off this becoming cross-browser?
    Ton

  4. 4
    Steve Melcher Says:

    Much simpler method… use:

    a:not([href*="underscorebleach.net"]):after { content:” (offsite)”; }