Creating Responsive Image Maps
December 31, 2017Working on a browser, I constantly get to research and play with new, cutting-edge features. It can be a ton of fun to picture what the future of the web will look like and what new technologies we need to create to get there. But one of my passions is looking back at where the web has come from, to think about how older features can exist in today's world (and vice-versa). Whether it be making ServiceWorker powered websites work in IE 5, or client side applications work in Mosiac 2 browsers have done a phenomenal job of giving developers the ability to give rich backwards compatibility to their applications and sites. A lot of the time, if you code to standards, things just sort-of... work. This backwards compatibility has created the web where you can layer up offline strategies from a service worker, falling back to appcache, and further still to an HTA (if you are a sadist). However, the reason I still have a job is because a number of features from yesteryear simply were not created in a way that makes them of practical use for today's web, despite how useful their concepts may be. There have been countless examples of browsers trying something early on, and eventually getting it right (or at least better). Other times, there was a great feature back in the day, that simply doesn't have a modern analog. One of the biggest examples of this, is the humble image map.
Primer
In case you are unfamiliar, image maps are awesome! The idea is that you can define regions of an image that, when clicked, does different things. At least it did eventually.
The Stone Ages
The earliest concept of an image map was proposed by Tony Sanders back in 1996, and was first shipped in Mosiac 1.1. It was a lot less feature filled than future versions, however. Back then, rather than defining a shape client side like in the above example, every click just sent the coordinates of the pixel relative to the image back up to the server.
To get it to work, you would just take any image
<img src="./smile.gif"/>
add the ismap
attribute to it, and wrap it in an <a>
nchor tag like so.
<a href="cgi-bin/smile.map.html">
<img src="./smile.gif" ismap/>
</a>
and that was it! So when you click on the upper right left corner of the image the server gets a request at cgi-bin/smile.map.html?0,0
rather than just cgi-bin/smile.map.html
.
Now all you need to do to set the proper mood is slap together a perl script to process those coordinates, play with a tomagochi and walk in the snow, uphill, both ways.
Not too long after ismap
was created, browsers began to support clientside imagemaps.
Medieval Maps
The idea of having to bounce back between the client, the server, and back to the client to do a what is basically just a link navigation is kind of a bummer. You are doubling the number of server trips for something that could probably be fully expressed in the page's original html. Coupling that with the speed of the internet 20 years ago, and you have a you can see how much of a perf problem image maps could be. So in HTML 3.2, we gained the ability to define the click regions for an image map on the client side. Gone are the days where you had to wrap an image in an anchor tag, now you get new semantic elements!
<img src="./smile.gif" usemap="#SmileMap"/>
<map name="SmileMap">
<area shape="rect" coords="0, 0, 120, 250" href="./pageOne.html">
<area shape="circle" coords="275, 75, 75" href="./pageTwo.html">
</map>
If you have ever written SVG, you may get recognize some of the syntax. The idea is that you create a <map>
element, and inside of it you write some <area>
tags that create the regions over the image. Sort of like absolutely positioning anchor tags over an image, but more flexible, because you are not limited to rectangles. You can use circles, as well as polygons. That means that you can create extremely detailed outlines of items in an image in order to create a rich, client side, javascript free experience for users that can work in browsers older than a lot of our readers. Pretty neat!
an aside...
One of the reasons I first got really into image maps is my background in ecommerce. Back in the day, I worked at Urban Outfitters and a very common request from management was to have an image on the site, and have our users be able to click on any of the stuff in the image and take them to the page to buy that thing. Seems like a perfect fit for image maps, right? Thats what my buddy Jack Keller and I thought when we started to code the thing up. But quickly we realized a problem you may have already noticed. If you haven't, check out how you define the coordinates.
<area shape="rect" coords="0, 0, 120, 250" href="./pageOne.html">
Those numbers inside the coords
section are pixels. It means that the rectangle starts at 0,0 (the top left of the image), and extends to the right 120 pixels, and down 250 pixels. Makes perfect sense in a world where every website was hard coded 640 x 480, but when you live in the 21st century, we needed to be coding responsive websites rather than hardcoding exact coordinates into the design.
So, I told my coworkers it wasn't possible, and we retreated1. We ended up putting absolute positioned anchor tags over an <img>
, and pouting about it. Image maps felt so close to being the right thing, it was just not quite there. But it is a problem that always stuck in my craw. In the back of my head I never stopped trying to figure out how to get responsive image maps working.
Thank god for Estelle
Years later, I was catching up on the blogs that I follow, when something caught my eye. This was a time when browsers still lacked support for the <picture>
element or srcset
, and as a result there was not a way to load different sized images for different sized devices in an accessible way - at least that was the common thought. Estelle Weyl, however, had discovered a way to achieve just that using SVG. She dubbed it the "Clown Car" technique, and essentially the idea is that you use SVG's ability to embed CSS to include a @media
query that will change the image that the user sees based on the window's screen size. It was a incredibly clever solution to the problem, but once we had actual support for responsive images (e.g. srcset
) there wasn't really a reason to use it anymore. Or so I thought.
Responsive Image Maps
The thing that makes the clown car technique so cool is that it is powered by SVG. SVG is extremely flexible, and as a result, we are able to leverage it to create an image map-like thing that is actually useful in todays responsive world.
As we covered before, image maps use exact pixels for coordinates. As a result, we aren't able to salvage them to use for our modern implementation. But we take the spirit of what they were used to achieve, and pair it with Estelle's clown car example, and get something great.
Here is what you need to get a basic version working
See the Pen Responsive Image Map by patrick kettner (@patrickkettner) on CodePen.
It works! Try resizing your window, or just collapsing or opening the codepen tabs up above. No matter what the size of the image, the <path>
s have scaled properly and give us the desired effect. Hopefully how it works is pretty self evident by looking over the code, but I will cover some of the more potentially esoteric portions of it.
viewBox
Right off the bat, the <svg> tag has the viewBox
attribute on it. This is a part of the SVG Coordinates system, which Sara Soueidan covers wonderfully here. I wouldn't dare attempt to summarize Sara's work (you are doing yourself a disservice if you haven't read it), suffice to say that it helps maintain the aspect ratio of the images we load inside of our image map.
path { fill: transparent }
Each of the clickable areas in our image map are inside of <path>
tags. By default, their fill
(sort of like an SVG version of background-color
) is black. Since we want our users to actually be able to see what they are clicking on, we make it transparent
xlink:href
While we do use an <a>
tag (becuase it works in both SVG and HTML documents), we need to go back in time to gross XML documents to support the ability to actually link to something with our links. You can read a bit more about them here, but essentially you just need to include xlink:
before your href
s.
Making it Accessible
As excited as I was after stumbling onto this, I noticed a number of potential improvements that could be made. For our second version, we introduce a number of accessibility related changes
See the Pen A11Y Responsive Image Map by patrick kettner (@patrickkettner) on CodePen.
so what did we change?
Pointing out links
Traditional image maps show the pointing-finger cursor whenever you are hovering over a link. By default, our SVG based version does not. This is easily remedied by setting the cursor
CSS property.
path {
cursor: pointer
}
Using <g>roups, setting <title>s
One thing I love using with traditional <a>
tags is that if you set a title
attribute, you will get a tooltip with the title whenever you hover over any of those links. We can replicate this by using the <title>
tag in our SVG code, but in order to do so we need to create a group. If you look over the above code, you will see that the <path>
s have been wrapped in <g>
tags. That way we can place a <title>
inside each of the <g>
s.
More svelt outlines
For our keyboard-navigating friends, the outline
CSS property is an essential way to show where the keyboards current focus is. outline
s are always rectangular. Normally this is fine, since div
s are as well. But since we have complex polygons, we can create highlights that hug the curves of our elements exactly. By setting a semi-opaque fill
for a focused path, we can hide the built in outline
while still allowing for the current focus to be clear to our user.
Doing this yourself
Hopefully how the code actually works has made a decent amount of sense. The next challange is to create all of the complex polygons for all of the different clickable areas. I go over how to do so in the video below, but the TL;DW is outlined in the text below
So how do you create these SVGs yourself? Once you have an image, open it up in Illustrator (or Inkscape if you prefer a free program). Both Illustrator and Inkscape are vector editing programs that we will use to trace the shapes of the image.
Using the pen tool, you can start to create a new shape on a new layer. Just outline each item you want to be a region, making sure to create a new layer for each region you want to be seperate.
Once that is complete, we can save our file as an SVG. Since we don't want to embed the image inside of it, make sure to delete the layer containing the image we have been tracing over. Now that we have our SVG file, it would be a great idea to compress the file. There is undoubtedly some reduction possible. I highly recommend SVGOMG, an online SVG minifier.After that - thats about it! Now you just need to wrap each of the <path>
s in a <g>
tags to add our title, and <a>
tags to get it to link to stuff.
And thats it! You can now create responsive image maps using inline SVG.
In conclusion...
The web has a long, and storied history. And having been built by mortals, it is filled with false starts, dead ends, and vestigial tails. But we can learn a lot about the right way to move forward when we examine the problems we have had in the past. Image maps are a really fun problem to solve, but there are lots more out there to do as well. What else did the old web have that the modern one lacks?
Please do reach out to me on twitter or email with comments and corrections.
As a result of the discovery of this article, I would like to formally apologize to both Jack and Todd. I was wrong, this was totally possible.