Interactive SVG map

I recently needed to present a map on a website so that visitors could choose their region (i.e. a group of countries, which could be arbitrary) before going into the site. After a little playing around, I settled on a solution that uses an SVG map of the world, and used CSS and Javascript to highlight the region and make it clickable.

The first challenge was how to create an interactive area that wasn’t a box. HTML is great, but it’s fundamentally boxy, even when you round the corners off. With some countries being really small, a box-based approach simply wasn’t going to work. That’s where SVG comes in – it’s a vector image in the browser. I managed to find an open-source SVG of the entire world which suited my purposes. I mean, it’s not a perfect map, but that’s the curse of the Mercator projection.

The important thing about this particular SVG is that it’s marked up with country codes. Since SVG is basically just XML, it can include more than just visual data, and in this case the author has included two-letter country codes in the image code. That’s awesome, because it means we can hook into it with other code! For example, here’s an extract showing the SVG markup for Greece, with the two-letter identifier gr .

<g cc="gr">
  <path d="M506.71,217.6l-0.11,1.33l4.63,2.33l2.21,0.85l-1.16,1.22l-2.58,0.26l-0.37,1.17l0.89,2.01l2.89,1.54l1.26,0.11l0.16-3.45l1.89-2.28l-5.16-6.1l0.68-2.07l1.21-0.05l1.84,1.48l1.16-0.58l0.37-2.07l5.42,0.05l0.21-3.18l-2.26,1.59l-6.63-0.16l-4.31,2.23L506.71,217.6L506.71,217.6z"/>
  <path d="M516.76,230.59l1.63,0.05l0.68,1.01h2.37l1.58-0.58l0.53,0.64l-1.05,1.38l-4.63,0.16l-0.84-1.11l-0.89-0.53L516.76,230.59L516.76,230.59z"/>

However, note that you need the SVG source in your HTML document for this to work. If it’s not in the DOM, the browser will treat it like an embedded object or an iframe, and you won’t be able to do anything with it. For the project I was working on, I used PHP to include the file contents into my page. An alternative approach, which I used for this demonstration, is to use Javascript to load the image into the page. You can use either approach. Or something entirely different. Whatever floats your boat. Just make sure the SVG markup ends up in your DOM.


Once it’s there we can start manipulating it. The first thing I did was play around with the styles to make it look a little nicer for my context. A little CSS targeting the path elements is all you need, but note that unlike regular HTML elements you’re going to want to set the fill  and stroke  rather than background  and border . You can use mouse cursor styles too, and transitions, and undoubtedly a whole lot more besides.

.map {
  max-width: 100%;
  path {
    fill: #c0c0c0;
    stroke: none;

Now comes the Javascript. We start off with a list of country codes we want to group together. I started with a few countries in Europe, but you could use anything you like, using the two-letter country codes. Essentially, when we detect that the mouse has entered an SVG path (or group of paths) we check to see if that path (or group) has a country identifier in our region array. If it is, we apply an .active  class to it, and use CSS to change its colour.

var region = ['gb', 'fr', 'de', 'es', 'it', 'nl', 'ie', 'be', 'pt', 'ch'];

$('.map').on('mouseenter', 'path, g', function() {
  var thisCountry = $(this).attr('cc');
  if (region.indexOf(thisCountry) > -1) {
    $.each(region, function(index, country) {
      $('.map [cc="' + country + '"]').addClass('active');
}).on('mouseleave', 'path, g', function() {
  $('.map .active').removeClass('active');
.active, .active path {
  fill: blue;

I then took that a little further with my demo and catered for multiple regions, just for laughs. We’re using the region index to apply a further class, for example .group1 . Then in the styles we want to give each group a different colour, so we’re defining an array of colours and using a SASS mixin to generate as many variations as we have colours in our list.

// Define our colour scheme as a list
$colors: cornflowerblue, indianred, forestgreen, gold, lightsalmon;

// Apply colour to the right group
@mixin path-colors {
  @for $i from 1 through length($colors) {{$i - 1},{$i - 1} path {
      fill: nth($colors, $i);

.map {
  @include path-colors();

Finally, when we click on a country/region we want to do something with it. For the purpose of this demo, I’m just using the index in the class name to look up a name in another array and showing it as a message, but you could just as easily route that to another action depending on your use-case.

Here’s the final demo showing it all at work.

I hope you find that useful. Let me know if you think there are any improvements I could make to the demo, or if you would have approached the problem a different way!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.