CSS has many powerful features, but one major limitation: changes to elements can only affect those that follow them. That is, hover
on an element can affect the next element, using an adjacent or sibling selector, or a child element with a descendant selector, but not any other element on the page. This limits CSS interactivity to certain modalities… and while there’s plenty of room within this space, forcing certain kinds of UI to work with pure CSS sometimes necessitates a degree of trickery and inspired hacks.
JavaScript has the ability to modify arbitrary elements on the page, linking an action here to an effect over there. CSS animations run “closer to the metal” than JavaScript manipulations, making smoother, swifter and cleaner transitions. When an effect would benefit from the strengths of both technologies, it makes sense to merge them.
A good example is my earlier creation of a modern imagemap that utilized CSS3 transitions to create popup information boxes. While it worked, it did so only to a fashion: the use of the CSS :target
pseudo-selector meant that current browsers would visually jump the page to the clicked portion of the imagemap due to a shared bug.
Rather than wait for the browser code to be fixed, a better solution right now would be to hybridize the effect: keep the pop-up elements as pure HTML, and retain the transitions, but use JavaScript to initiate the effect.
To demonstrate this idea I’ll create another imagemap, this time of old Montreal in the 1700s. You can see the imagemap hotspots highlighted in the illustration. I won’t cover this step here; if you’re interested, you can read how to make an imagemap in Dreamweaver.
Each area on the imagemap uses the freeform HTML5 data
attribute to create data-pop
, with a value that matches the id
of the associated <figure>
element. Each interior <figure>
contains information related to the highlighted area.
<figure id="oldmap">
<figure id="old-montreal">
<img src="basilique-notre-dame.jpg" alt="Photograph of Notre Dame">
<h4>Old Montreal</h4>
<figcaption>The site of the original French settlement, turned into a fortified city in 1721.</figcaption>
</figure>
<figure id="biodome">
<img src="ring-tailed-lemur.jpg" alt="Photograph of ring-tailed lemur">
<h4>Biodome</h4>
<figcaption>
Originally constructed as a velodrome and judo dojo for the 1976 Olympic Games, the dome was converted into a nature exhibit in 1992.
</figcaption>
</figure>
<img src="montreal.jpg" alt="Map of Montreal in 1700"
style="max-width:100%;height:auto;" usemap="#montreal">
<map name="montreal" style="position: relative">
<area shape="poly" alt="Old Montreal" data-pop=old-montreal
coords="577,282,562,270,521,290,531,309,557,307,575,298,578,231">
<area shape="poly" coords="586,320,595,300,590,288,570,312"
alt="Biodome" data-pop=biodome >
</map>
</figure>
Above that, I’ll create a few rules for the individual <figure>
elements to determine their position, an attribute selector to set them all to hidden (using opacity
) and a class (.popup
) that will be used to “pop” each <figure>
into existence:
figure#oldmap {
margin: 0;
padding: 0;
position: relative;
}
figure#oldmap figure {
width: 200px;
position: absolute;
background: rgba(238,238,238,0.78);
border: 1px solid rgba(177,177,177,0.8);
border-radius: 3px;
opacity: 0;
left: 203px;
top: 120px;
z-index: 99;
transform: scale(0.8);
transition: .4s all cubic-bezier(.06,.62,.73,1.5);
padding: 0;
}
figure#oldmap figure img {
width: 100%;
}
.popup {
opacity: 1;
transform: scale(1);
}
figure#oldmap figure h4 {
text-align: center;
margin: .5rem;
}
figure#oldmap figure figcaption {
padding: .5rem;
margin-top: 0;
margin-bottom: 1rem;
}
Finally the script at the end of the page:
$('area').click(function() {
var popup = $(this).attr('data-pop');
$('figure#oldmap figure').removeClass("popup");
$("#" + popup).addClass("popup");
return false;
});
The JQuery code is very simple: using the value of data-pop
from the area that is clicked on to determine the id
of the <figure>
to apply the popup
class to.
You’ll note that the script doesn’t immediately work, even though the class is applied, due to the fact that the class will not overrule the established CSS. One way to get around this is to add the recently-discussed !important
: to the declarations:
.popup {
opacity: 1 !important;
transform: scale(1) !important;
}
This brings the two technologies nicely together: the transition will occur due to the state of the element changing: in this case, through having a new class applied via JavaScript.
Map of old Quebec from Wikipedia, photographs by Doug and Eric Bégin. licensed under Creative Commons.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.