Links mit Inline-SVG, auf Ziel bleiben mit Events

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Es ist ziemlich üblich, SVG innerhalb eines Ankerlinks oder eines anderen „klick-/tippbaren Dings“ auf einer Webseite zu verwenden. Es ist auch zunehmend üblich, dass das SVG inline <svg> ist, da es oft schön ist, das SVG im DOM zu haben, da man es mit CSS gestalten und mit JS und so weiter skripten kann. Aber was bedeutet das für Klickereignisse?

Ein Link mit einem SVG-Icon darin könnte so aussehen

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

Jetzt möchten Sie ein Klickereignis an diesen Ankerlink binden. In jQuery

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

});

Das wird einwandfrei funktionieren. Beachten Sie, dass das Ankerlink ein data-*-Attribut hat. Das ist wahrscheinlich speziell dafür da, damit JavaScript darauf zugreifen und es verwenden kann. Kein Problem, wie wir es gerade geschrieben haben, denn innerhalb der anonymen Funktion, die wir gebunden haben, wird this immer dieser Ankerlink sein, der dieses Attribut zur Verfügung hat. Selbst wenn Sie Event-Delegation verwenden und eine Funktion aufrufen, wer-weiß-wo, um sie zu verarbeiten, wird this dieser Ankerlink sein und Sie können dieses data-*-Attribut leicht abgreifen.

Aber nehmen wir an, Sie wollen etwas rohes JavaScript-Event-Delegation rocken

document.addEventListener('click', doThing);

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

Sie haben dort keine einfache Referenz auf Ihren Ankerlink. Sie müssen das event.target testen, um zu sehen, ob es überhaupt der Ankerlink ist. Aber im Fall unseres SVG-in-einem-Link, was ist dieses Ziel?

Es könnte entweder sein

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

Sie müssen vielleicht nur den tagName des angeklickten Elements überprüfen und, wenn Sie wissen, dass es ein Unterelement war, die Kette nach oben gehen

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"));
}

Das ist aber ziemlich verrückt. Es ist zu stark an die HTML- und SVG-Struktur gebunden. Fügt man ein <g> drumherum um einige <path>s, was eine vollkommen in Ordnung Sache zum Gruppieren ist, und es bricht. Oder die tagType ist path und nicht rect, oder irgendein anderer DOM-Unterschied.

Persönlich bevorzuge ich einige CSS-Lösungen.

Eine Möglichkeit ist, ein Pseudo-Element über das gesamte Anker-Element zu legen, damit die Klicks garantiert auf dem Anker-Element selbst landen, nichts darin

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

Das scheint auch ziemlich gut zu funktionieren

a > svg {
  pointer-events: none;
}

pointer-events funktionieren typischerweise nicht in IE (in 11+ schon, aber nicht darunter), aber es funktioniert tatsächlich, wenn es auf SVG angewendet wird, in IE 9+, was die Version von IE ist, die SVG sowieso unterstützt.

Hier ist ein Pen mit dem Problem demonstriert und die Lösungen

Siehe den Pen owyFj von Chris Coyier (@chriscoyier) auf CodePen.

Punkt ist

Wenn Sie SVG in einem Klickziel verwenden, seien Sie sehr vorsichtig, dass Sie das richtige Element in JavaScript erhalten, und wenn Sie Probleme haben, erwägen Sie eine CSS-Abdeckung.