Wie SVG-Fragmentidentifikatoren funktionieren

Avatar of Chris Coyier
Chris Coyier am

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

Ich habe hier schon viel über das SVG-<use>-Element gesprochen und wie man es für den Aufbau eines Icon-Systems nutzt. Das Schöne am <use>-Element ist, dass man nur einen Teil eines woanders definierten SVGs referenzieren und nur diesen Teil woanders zeichnen kann. Diese Fähigkeit ermöglicht es, ein ganzes System daraus aufzubauen und das Problem des „viele Bilder in einer Anfrage, weil das super effizient ist“, zu lösen, das wir früher mit CSS-Sprites und Icon-Fonts gelöst haben.

Aber <use> bedeutet Inline-SVG. Es hilft nicht, wenn man einen Teil eines größeren SVGs in SVG-als-<img> oder SVG-als-background-image verwenden möchte. Hier kommen Fragmentidentifikatoren ins Spiel.

Vorbereitung des SVG darauf

Eine Möglichkeit, dies zu tun, ist, das SVG (Sprite, denke ich, nennen wir es einfach Sprite) wie ein grafisches „CSS“-Sprite anzuordnen.

Wir tun dies speziell, weil wir letztendlich einige viewBox-Zahlen verschieben werden, um nur einen Teil dieses Bildes anzuzeigen, genau wie wir es früher mit CSS-Sprites getan haben.

In dieser kleinen Mini-Demo verwenden wir drei Icons, die jeweils 32×32 Pixel groß sind. Das Dokument ist also 32×96 Pixel groß. Wir könnten die viewBox-Spielereien so betrachten:

Gesamtes Dokument viewBox 0 0 32 96
Nur oberes Icon anzeigen viewBox 0 0 32 32
Nur mittleres Icon anzeigen viewBox 0 32 32 32
Nur unteres Icon anzeigen viewBox 0 64 32 32

Das Attribut viewBox lautet: links, oben, breite, höhe. Beachten Sie das zweite Attribut, oben, das jeweils um 32 erhöht wird. Wir zeigen einfach einen anderen Teil des gesamten Dokuments an.

Hinzufügen dieser speziellen viewBoxs zum SVG selbst

Sie können diese speziellen, spezifischen viewBox-Werte in einem <view>-Element in das SVG einfügen, das speziell dafür gedacht ist.

<view id="icon-clock-view" viewBox="0 0 32 32" />
<view id="icon-heart-view" viewBox="0 32 32 32" />
<view id="icon-arrow-right-view" viewBox="0 64 32 32" />

Nun können wir diese Werte von woanders referenzieren und verwenden.

Die <view>-Elemente können eigenständig so stehen oder andere Elemente umschließen. In diesem Fall wird die viewBox wirksam, wenn die ID übereinstimmt, also:

<!-- this viewBox takes over if current fragment identifer is #match-me -->
<view id="match-me" viewBox="0 64 32 32">
  <rect ...>
</view>

Demo dieser Art von Funktion in der Spezifikation.

Syntax für HTML

Um diese speziellen viewBox-Werte auf SVG als <img> anzuwenden, könnten Sie es so machen:

<!-- top icon -->
<img src="sprite.svg#svgView(viewBox(0, 0, 32, 32))" alt="">

Oder, wenn Sie bereits <view>-Elemente eingerichtet haben, können Sie sie einfach nach Namen referenzieren:

<!-- middle icon -->
<img src="sprite.svg#icon-heart-view" alt="">

Syntax für CSS

Sie können eine spezielle viewBox direkt im Bildpfad in CSS deklarieren:

.icon-clock {
  background: url("sprite.svg#svgView(viewBox(0, 0, 32, 32))") no-repeat;
}

Oder ein <view>-Element referenzieren, wenn Sie diese eingerichtet haben:

.icon-clock {
  background: url(sprite.svg#icon-clock-view) no-repeat;
}

Obwohl… wenn Sie SVG über CSS auf diese Weise verwenden und sich die Mühe gemacht haben, das SVG auf diese Weise auseinanderzulegen, möchten Sie vielleicht einfach die CSS-Sprite-Technik verwenden und den Hintergrund verschieben.

.icon-heart {
  background: url("sprite.svg") no-repeat;
  background-size: 32px 96px;
  background-position: 0 -32px;
}

Ich möchte die Icons einfach übereinander stapeln.

Wenn die Icons alle die gleiche viewBox haben und Sie sie im Grunde nur nach Bedarf ein-/ausblenden möchten, kann dies etwas einfacher werden.

Gestalten Sie sie alle im gleichen Bereich (oder verwenden Sie ein Build-Tool, das dies tut, oder was auch immer). Hier platziere ich jedes Icon in eine eigene Gruppe mit einer eindeutigen ID.

Der Trick, um das Ein-/Ausblenden zu ermöglichen, besteht dann darin, etwas CSS einzubetten, das 1) alles ausblendet und 2) das mit einem übereinstimmenden Fragmentidentifikator einblendet. CSS ist dafür geeignet, wegen des :target-Selectors.

Alles zusammen im SVG

<defs>
  <style>
    g {
      display: none;
    }
    g:target {
      display: inline;
    }
  </style>
</defs>

<g id="icon-clock">
  <path d="M20.6,23.3L14,16.7V7.9h4v7.2l5.4,5.4L20.6,23.3z M16-0.1c-8.8,0-16,7.2-16,16s7.2,16,16,16s16-7.2,16-16S24.8-0.1,16-0.1z
		 M16,27.9c-6.6,0-12-5.4-12-12s5.4-12,12-12s12,5.4,12,12S22.6,27.9,16,27.9z"/>
</g>
<g id="icon-heart">
  <path d="M32,11.2c0,2.7-1.2,5.1-3,6.8l0,0L19,28c-1,1-2,2-3,2s-2-1-3-2L3,18c-1.9-1.7-3-4.1-3-6.8C0,6.1,4.1,2,9.2,2
		c2.7,0,5.1,1.2,6.8,3c1.7-1.9,4.1-3,6.8-3C27.9,1.9,32,6.1,32,11.2z"/>
</g>
<g id="icon-arrow-right">
  <path d="M32,15.9l-16-16v10H0v12h16v10L32,15.9z"/>
</g>

Browser-Unterstützung

Can I Use verfolgt die Unterstützung für Fragmentidentifikatoren. Leider ist es nicht ganz so einfach wie funktioniert oder nicht, denn je nach Browser funktioniert es möglicherweise in HTML und nicht in CSS oder hat Eigenheiten.

Hier ist meine Testseite

Siehe den Pen SVG Fragment Identifiers in HTML and CSS von Chris Coyier (@chriscoyier) auf CodePen.

Ich habe keine super detaillierten Browser-Support-Notizen dafür gemacht, aber hier sind die Highlights:

  • Firefox macht alles richtig.
  • IE 11 macht alles richtig. IE 9 & 10 sind mit background-position etwas seltsam (Quetschen), aber ansonsten funktioniert alles.
  • Aktuelle Versionen von Chrome/Safari/Opera (38/8/25) behandeln alle HTML-<img>-Techniken gut, aber keine der CSS-Techniken, einschließlich der background-position-Methode. Pre-Blink Opera war interessanterweise dasselbe. Update: Wenn das referenzierte <svg> korrekte Breiten- und Höhenattribute hat, funktionieren alle Methoden wie erwartet. Zumindest jetzt, mit Chrome (56) / Opera.
  • Das Einzige, was iOS 8.1 verarbeiten kann, ist <img>, das auf ein <view> verweist.
  • Das Einzige, was Android 4.4 richtig macht, ist background-position (diejenige, die eigentlich gar keine Fragmentidentifikatoren verwendet). Android 5 entspricht dem aktuellen Chrome/Safari/Opera wie oben.

Verlinkung!