Using attr(), with pseudo-elements and JavaScript
One of the lesser-used features of CSS is the attr()
function. Like calc()
, it's used to set the value of a CSS property. Though first defined as a value for the content
property in CSS2, it's since been re-defined as a value for just about any CSS property.
I'm getting ahead of the state of support, though. Right now, we can only use attr()
with content
. What does it do? attr()
accepts an HTML attribute as its argument, and returns the value of that attribute as the value for that property. This doesn't seem like such a big deal, I'm sure. But it's actually pretty useful for setting the content of pseudo-elements to create text effects. Take the following HTML.
<p data-txt="This is amazing!">This is amazing!</p>
We've set a data-txt
attribute is an exact copy of the text contained between the <p>
and </p>
tags. Now in our CSS, we can use the attr()
function.
p {
background: #f3c;
width: 50%;
margin: auto;
}
p::before, p::after {
content: attr(data-txt);
display: block;
}
p::before{
background: #9fc;
}
p::after{
background: #9cf;
}
The result should resemble what follows below.
Adrian Roselli used this to brilliant effect in his 2012 demo and blog post Chromatic Type with Pseudo Elements.
This is all well and good, but perhaps you like to keep your HTML a little bit cleaner, or you don't want to remember to add a data-*
attribute. Well, we can take this one step further with some DOM scripting. The approach is simple: we'll set a data-txt
attribute using DOM scripting.
window.addEventListener('DOMContentLoaded', function(){
var p = document.querySelector('p');
p.dataset.txt = p.innerHTML;
},false); // The third parameter is optional in modern browsers
Here we've just copied the innerHTML
of our paragraph element to the data-txt
attribute when the DOMContentLoaded
event fires. This works in Safari, Chrome, and Firefox. But Internet Explorer requires a little bit more.
To make this work in Internet Explorer, we need to change our selectors to class selectors instead of element selectors.
.triptych {
background: #f3c;
width: 50%;
margin: auto;
}
.triptych::before, .triptych::after {
content: attr(data-txt);
display: block;
}
.triptych::before{
background: #9fc;
}
.triptych::after{
background: #9cf;
}
Then we need to add another line to our DOMContentLoaded
handler.
window.addEventListener('DOMContentLoaded', function(){
var p = document.querySelector('p');
p.dataset.txt = p.innerHTML;
p.classList.add('triptych');
},false);
This delays the effect until after the data-txt
attribute has been set. Once our data-txt
attribute is available on the p
element, we can add the triptych
class and resulting effect.
This works for multiple elements too. In the DOMContentLoaded
handler, you would just use document.querySelectorAll
and either Array.prototype.map
or a for
loop to add a class name to each element.