Links with Inline SVG, Staying on Target with Events

Avatar of Chris Coyier
Chris Coyier on (Updated on )

It’s pretty common to use SVG within an anchor link or otherwise “click/tappable thing” on a web page. It’s also increasingly common that the SVG is inline <svg>, because it’s often nice having the SVG in the DOM since you can style it with CSS and script it with JS and such. But what does that mean for click events?

A link with an SVG icon in it might be like this:

<a href="#0" data-data="something">
  <svg ... >
     <rect ...>
  <svg>
</a>

Now you want to bind a click event to that anchor link. In jQuery:

$("a").on("click", function(event) {
  
   // `this` will always be the <a>
  console.log($(this).data("data"));
  // "something"

});

That will work perfectly fine. Note there is a data-* attribute on the anchor link. That’s probably there specifically for JavaScript to access and use. No problem at all how we have it written right now, because within the anonymous function we have bound, this will always be that anchor link, which has that attribute available. Even if you use event delegation and call some function who-knows-where to handle it, this will be that anchor link and you can easily snag that data-* attribute.

But let’s say you’re going to rock some raw JavaScript event delegation:

document.addEventListener('click', doThing);

function doThing(event) {
  // test for an element match here
}

You don’t have an easy reference to your anchor link there. You’ll need to test the event.target to see if it even is the anchor link. But in the case of our SVG-in-a-link, what is that target?

It could either be:

  1. The <a>
  2. The <svg>
  3. The <rect>

You might just have to check the tagName of the element that was clicked, and if you know it was a sub-element, move up the chain:

document.addEventListener('click', doThing);

function doThing(event) {
  var el;
  
  // we can check the tag type, and if it's not the <a>, move up.
  if (event.target.tagType == "rect") {
    // move up TWICE
    el = event.target.parentElement.parentElement;
  } else if (event.target.tagType == "svg") {
    // move up ONCE
    el = event.target.parentElement;
  } else {
    el = event.target;
  }
  console.log(el.getAttribute("data-data"));
}

That’s pretty nuts though. It’s too tied to the HTML and SVG structure. Toss a <g> in there around some <path>s, which is a perfectly fine thing to do for grouping, and it breaks. Or the tagType is path not a rect, or any other DOM difference.

Personally I’ve been preferring some CSS solutions.

One way is to lay a pseudo element over top the entire anchor element, so that the clicks are guaranteed to be on the anchor element itself, nothing inside it:

a {
  position: relative;
}
a::after {
  content: "";
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

This seems to work pretty well too:

a > svg {
  pointer-events: none;
}

pointer-events typically doesn’t work in IE (it does in 11+, but not lower), but it actually does when applied to SVG, in IE 9+, which is the version of IE that supports SVG anyway.

Here’s a Pen with the issue demonstrated and the fixes:

See the Pen owyFj by Chris Coyier (@chriscoyier) on CodePen.

Point is

If you’re using SVG in a click target, be very careful you’re getting the right element in JavaScript, and if you have trouble, consider a CSS cover.