Fun with pseudo‑elements

I've known for quite a while that, you could inject content into HTML elements using CSS pseudo‑elements: ::before and ::after. But, they are way more useful than I originally thought…


The Simple Stuff

Simply put, pseudo‑elements let you inject CSS content immediately before or immediately after the content of the matched element.

For example, suppose I want all paragraphs in my document to start with the phrase "Yo dog, ". I could write:

Xzibit would be proud!
p::before { content: "Yo dog, "; }

Instead of using a string, one can also link to another resource using the url() css function. Here is a style that matches any anchor with an absolute url and injects an icon after them.

Adding images as content.
a[href^="http://"]:after, a[href^="https://"]:after {
  content: url('path-to-external-icon.png');
}

Got it! Anything else?

Yea!

The other day I ended up on MDN page for the content property. I noticed something new: the attr() value.

The attr() value returns the value of the attribute on the element as a string. I can make a context hover for external links to display thier href by doing something like this:

Or… you could just use title attribute.
a { position: relative; }

a[href^="http://"]:hover:after, a[href^=https://]:hover:after {
  /* Display the href attribute */
  content: attr(href);

  /* Put it in a box */
  display: block;
  position: absolute;
  padding: 1ex;
  border: 1px solid #666;
  background-color: #ccc;
}

And now you have a no-js hover for links…

One step further!

Things got more interesting when I realized I could do the same with HTML5 data- attributes. Consider the following markup:

<div class="greet" data-greeting="Hola">Jim</div>

Then I could do something like this:

.greet:before {
  content: attr(data-greeting) " ";
}

Aparently this was old news. A quick google search turned up a bunch of people talking about it a while ago, but it was news to me! I'm not sure where I would get to use it, but I do like that it helps keep the DOM pretty and it makes it simple to update certain things from javascript.

var forEach = Function.prototype.call.bind(Array.prototype.forEach),
    changeToHello = function(elem) {
      elem.dataset.greeting = "Hello";
    });

var greetings = document.querySelectorAll(".greet");
forEach(greetings, changeToHello);

It almost shadow-dom-esque. It's nice not having to know the internals of how the element is going to consume the data- attribute, one can just update the elements' state and be done.

The future and beyond…

There is one unimplemented feature that would also help make this technique a lot more powerful: the optional [type] argument of the attr() function. The spec defines such behavior but, as of writing this no browsers implement it.

This would be great!
.hasIcon:before {
  content: attr(data-icon-url url);
}

This would return the value of the data-icon-url attribute, but intrept it's value as a url rather than just a string. It would be pretty useful for injecting icons…