SVG hat ein <use>-Element, das im Wesentlichen bedeutet: Finde den SVG-Teil mit dieser #Kennung, klone ihn und füge ihn hier ein. Es ist eine wesentliche Zutat für ein SVG-Icon-System. Es gibt ein paar Dinge zu wissen, die wir bisher nicht behandelt haben.
Zur Erinnerung, es sieht so aus
<!-- Reference IN THIS SAME DOCUMENT -->
<svg>
<use xlink:href="#icon-1"></use>
</svg>
<!-- EXTERNAL reference -->
<svg>
<use xlink:href="sprite.svg#icon-1"></use>
</svg>
Diese #icon-1 Kennung verweist wahrscheinlich auf ein Symbol in dieser Datei, wie…
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-1" viewBox="0 0 1024 1024">
<title>Kinda like alt text</title>
<path class="path-1" d="..."></path>
</symbol>
...
</svg>
Der Vorteil einer externen Referenz ist, dass sie den Browser-Cache gut nutzt.
Außerdem ist sie einfach zu handhaben. Sie geben den korrekten Dateipfad zur Datei (dem „SVG-Sprite“) an und verweisen auf eine Kennung, und es funktioniert einfach. Sie können Ihren Server so einrichten, dass diese Datei mit allen korrekten Headern ausgeliefert wird, sodass der Browser sie wie jeden anderen zu cachenen Asset behält.
Das Problem war IE, aber es wird behoben.
Die meisten Browser, die Inline-SVG unterstützten, unterstützten dies, sodass es fast nutzbar war, mit der großen Ausnahme von IE. Aber sie haben dies nun in Microsoft Edge behoben. Edge hat noch keinen riesigen Marktanteil, aber es ist die Zukunft unter Windows, sodass wir dies schließlich ohne zusätzliche Arbeit nutzen können werden.
Aufgrund der mangelnden perfekten Unterstützung gibt es zwei wichtige Umgehungsmöglichkeiten.
- Fügen Sie das SVG-Sprite in alle HTML-Dokumente selbst ein. Funktioniert super. Ist tendenziell sehr schnell, bläht aber den Seiten-Cache. Und um die beste Unterstützung zu erhalten, müssen Sie es am Anfang des Dokuments einfügen, was zu einer leicht verzögerten Darstellung von wahrscheinlich wichtigerem Inhalt führt. Oder…
- Ajax für das Sprite. Das dann den Browser-Cache nutzen kann. Es kann jedoch schwierig sein, dies ohne ein wenig FONI (Flash of No Icons) zu tun.
Sobald wir externe Verweise direkt nutzen können, ist es nicht dasselbe
Dieses Konzept entzog sich mir und deshalb wollte ich diesen Artikel schreiben.
Ich dachte, die externe Referenz sei die ultimative Lösung, weil sie alles tun könne, was das Inline-SVG bei der Referenzierung von SVG im selben Dokument tun kann. Aber leider kann sie das nicht. SVG-Referenzen auf diese Weise haben ihr eigenes separates DOM. Es geht über die normale Shadow-DOM-Grenze hinaus, der alle <use> unterliegen.
durch das
<svg class="icon-1">
<use xlink:href="#icon-1"></use>
</svg>
Sie könnten CSS (im selben Stylesheet wie für den Rest der Website) schreiben, um es einzufärben
/* This works.
It will cascade this fill through the shapes,
as long as there are no presentational fill
attributes on the shapes themselves. */
.icon-1 {
fill: red;
}
Das kann man mit einem extern referenzierten <use> tatsächlich immer noch tun. Aber man kann einzelne Formen nicht mehr so stylen wie zuvor.
/* You could reach individual shapes
to style because they share the same DOM.
But this WON'T WORK with externally referenced SVG. */
.path-1 {
fill: yellow;
}
/* This won't work either way,
because it crosses a shadow DOM boundary */
.icon-1 /* ~shadow~ */ .path-1 {
fill: yellow;
}
Man kommt überhaupt nicht an die internen Formen heran, wenn man extern referenziert. Zum Beispiel vom HTML-Dokument aus
<script>
var shape = document.querySelectorAll(".path-1");
console.log(shape);
// [ ] (empty set)
</script>
Hier ist ein Gist, der den Punkt verdeutlicht.
Es ist immer noch ziemlich cool.
Die Tatsache, dass die Kaskadierung in einer einzelnen Farbe immer noch funktioniert, macht es ziemlich nützlich. Die meisten Icons sind tendenziell einfarbig. Und Sie können immer noch *unterschiedliche* Einzelfarben haben.
Seltsame Zukunfts-Sachen
Tab Atkins hat in seiner verrückten Vorausschau einige potenzielle Ideen dokumentiert, genannt SVG-Parameter
.foo {
background-image: url("http://example.com/image.svg" param(--color var(--primary-color)));
}
Das ist ein CSS-Beispiel, aber vermutlich könnte Inline-SVG dies auch nutzen.
Der Query-String vergisst den Punkt (
class) :pDanke! Behoben und vergrabe das, da es kein Problem mehr ist.
Hallo Chris, gute Arbeit.
Nur zur Erinnerung für Ihre Leser: Wenn Sie
<use>verwenden, können Sie Ihr SVG immer noch mit zwei Parametern einfärben, indem Sie die CSS-Eigenschaften fill und color verwenden und den Wert current-color in Ihrem SVG festlegen. Sie haben es hier besser erklärt als ich https://css-tricks.de/cascading-svg-fill-color/Ich arbeite gerade an einem Projekt mit dieser Technik und sie passt gut zu meinen Bedürfnissen (alle meine Icons haben maximal 3 Farben, also verwende ich fill, color und background-color auf dem Wrapper des SVG über transparente Bereiche im SVG.)
Bitte beachten Sie, dass externe Hosts (wenn Sie das Sprite von einem CDN ausliefern möchten) nicht unbedingt funktionieren, da CORS für solche Anfragen in den meisten Browsern nicht implementiert ist: http://tympanus.net/codrops/2015/07/16/styling-svg-use-content-css/comment-page-1/#comment-466665
Ja, das ist sehr wichtig!
Man würde denken, CORS-Header würden dies erlauben, aber kein Glück. Ich hoffe, das wird als Fehler betrachtet, der behoben wird, aber ich bin mir nicht sicher (vielleicht ist es zu gefährlich, als dass es jemals erlaubt würde?)
Sie können also auf eine externe URL verweisen, aber nur auf dieselbe Domain.
Nun, es ist ein gemeldeter Fehler in Chromium (nicht sicher für die anderen), aber es gab nicht viel Aktivität dazu: https://code.google.com/p/chromium/issues/detail?id=470601. Ich hoffe immer noch, dass es bald behoben wird.
Ich sehe keine Risiken, wenn dies mit korrektem CORS und CSP erlaubt ist.
Ich habe eine Lösung gefunden, um ein externes Sprite-Sheet zu haben, das
useundsymbolverwendet und jedes Element innerhalb des Symbols mit normalem CSS stylen kann, als wäre es inline. Das Beste daran ist, dass es in allen Browsern funktioniert und Sie wahrscheinlich bereits die richtige Bibliothek verwenden.Was svg4everybody tut, ist, die
use-Tags durch den tatsächlichen Inhalt des Symbols zu ersetzen – aber nur in IE und alten Safari-Versionen. Durch Setzen vonpolyfill:trueerzwingen Sie dieses Verhalten in allen Browsern.Ich habe es in https://github.com/jonathantneal/svg4everybody/issues/82 vorgeschlagen und verwende es erfolgreich auf http://toyotahalloffame.com
Vermutlich, wenn wir aufhören, svgforeverybody zu verwenden, weil Old IE nicht mehr unterstützt werden muss, könnte jemand diese Funktionalität in ein kleineres Polyfill extrahieren, das wir alle verwenden können? Es liegt wahrscheinlich jenseits meiner Fähigkeiten, den svg4everybody-Code zu analysieren und herauszufinden, wo die Magie liegt, aber ich bin sicher, es gibt jemanden, der ein kleines Polyfill erstellen könnte, das uns dabei hilft?
Noch besser: Dann ist jede Instanz *vollständig getrennt*, sodass Sie sogar mehrere interne Formen unterschiedlich stylen könnten. Das geht auf Kosten von 1) einer zusätzlichen (kleinen) Bibliothek 2) mindestens einer XHR-Anfrage (macht sie eine pro Icon oder eine insgesamt?) 3) DOM-Gewicht
Jon: Ich glaube nicht, dass es etwas zum Extrahieren gibt; svg4everybody macht genau das, aber standardmäßig tut es dies nur als „Polyfill“. Die einzige Verbesserung, die vorgenommen werden könnte, wäre die Entfernung der UA-Prüfung.
Das ist nicht der Grund. Der Grund ist, dass CSS-Regeln nicht dateiübergreifend angewendet werden. Sie können es nicht stylen, weil es sich in einer separaten Datei befindet.
Das ist genau das, was ich meine. Es ist ein anderes DOM. Aber man kann dieses DOM immer noch sehen, zum Beispiel, wenn man es inspiziert. Es sieht also genau aus wie ein normales
<use>mit Shadow-DOM und allem, aber es folgt anderen Regeln.Nein, der Browser ruft die externe Datei ab und dupliziert die verwendeten
<symbol>s innerhalb des Dokuments als Shadow-DOM. So dass dies in Ihrem *deklarierten HTML*… in das Folgende im *Dokument* umgewandelt wird
… und Sie können nicht durch die Shadow-Root auswählen.
Nun, manchmal kann man das
In Firefox ist die
<use>-Implementierung etwas alt, liegt weit vor den Shadow-DOM-Spezifikationen und lässt Sie die „kopierten“ Elemente auswählen. Was an sich ein Fehler ist, und es kann auch Fehler in Ihrem eigenen Code verursachen, denn wenn Siesvg { fill: red; }schreiben, wird es auch auf das innere SVG-Element angewendet, und wenn Sie später die Farbe eines bestimmten Icons z. B. mit.cool-icon { fill: blue; }ändern wollen, bleibt das Icon rot.In Chrome können Sie den Shadow-Piercing-Kombinator verwenden, wie folgt:
svg /deep/ path { fill: green; }, aber diese experimentelle Funktion ist veraltet und soll zukünftig entfernt werden (gemäß einer Entscheidung beim letzten Web Components Face-to-Face-Meeting).Tolle Punkte, Chris,
Obwohl ich SVG (Sprites) liebe, gibt es viele Probleme zu berücksichtigen.
Noch etwas: Da die Leute unsere Codebeispiele kopieren und einfügen, wäre es großartig, wenn wir die robusteste und zugänglichste Markierung befürworten könnten, meiner Meinung nach.
Ich bin kein A11y-Experte, aber nach meinem Verständnis könnten/sollten wir zusätzliche Schritte unternehmen, um unsere SVG-Sprite-Icons zugänglicher zu machen.
Unten meine aktuellen Gedanken, ich würde mich über den Rat eines A11y-Experten freuen!
Erstens: Unterscheiden Sie zwischen „präsentationalen“ und „inhaltlichen“ Icons. Genau wie wir es bei
imgund ihrenalt-Attributen tun würden.Für alle SVG-Icon-Sprites (in
sprite.svg)Fügen Sie ein
title-*Element* als Kind zumsymboldes Sprites hinzuFügen Sie ein
desc-*Element* als Kind zumsymboldes Sprites hinzuNun, für die Verwendung dieser Symbole als rein präsentationsorientierte Icons
Verwenden Sie
role="presentation"auf dem SVGDas war's. Aber für SVG-Icons als „Inhalt“ sollten wir mehr tun
Fügen Sie
role="img"hinzuFügen Sie ein
title-*Attribut* hinzuFügen Sie ein
title-*Element* mit einer eindeutigenidhinzuFügen Sie
aria-labelledby="title-id"hinzuDas sorgt für die beste Barrierefreiheit, soweit ich weiß.
Nochmal: Ich bin kein A11y-Experte, also korrigieren Sie mich bitte, wenn ich falsch liege.
Informationsquellen
http://codepen.io/NathanPJF/full/GJObGm
https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/
https://github.com/jonathantneal/svg4everybody#readability-and-accessibility
Interessant, ich habe Ihren Gist heruntergeladen, konnte in der Vorschau aber nichts sehen.
Chrome: Zeigt grünen Spaten, schwarzen Stift
Firefox: Zeigt grünen Spaten, grünen Stift
IE: Zeigt nur den grünen Spaten
Haben Sie es mit einem echten Webserver ausgeführt? Das könnte notwendig sein.
Kann ich das zum Laufen bringen? =)
Entschuldigung, Demo =)
http://nezed.github.io/SVG-External-Reference-as-bg/
Ich habe dieses Problem vor ein paar Wochen behandelt. Ich habe einem Kunden meiner Firma eine Icon-Refaktorierung vorgeschlagen, um alte PNG-Sprites durch SVG zu ersetzen, da wir viel Anpassung benötigen.
Die externe Referenz war seit Chris' erstem Artikel darüber in meinen Gedanken, aber als ich erkannte, dass es einige mehrteilige Icons im System gab, die mit dieser Technik nicht individuell gestylt werden konnten, geriet ich in Panik.
Glücklicherweise habe ich einen Artikel von Osvaldas Valutis gefunden, der localStorage verwendet, um das Sprite zu cachen und es inline einzufügen. Wenn localStorage voll ist, lädt es das Sprite und fügt es trotzdem ein. So habe ich es gehandhabt, obwohl diese Technik nicht perfekt ist und die Darstellung von Icons um einen Bruchteil einer Sekunde verlangsamt.
Ich werde definitiv die Technik von **Federico Brigante** ausprobieren, die einen kleinen Trick von svg4everybody verwendet und sehen, welche besser abschneidet!
Mach weiter so, Chris!
Ein paar zusätzliche Punkte
Wie Anselm Hannemann oben bemerkt, gibt es derzeit keine Möglichkeit, CORS-Inhalte in SVG zu aktivieren. Es ist eine geplante Ergänzung der Spezifikation, aber noch nicht zuverlässig in Browsern unterstützt.
Obwohl die SVG 1.1-Spezifikation besagen würde, dass Stile, die in CSS *in der externen Datei* gesetzt werden, immer noch auf den geklonten Inhalt angewendet werden sollten, war dies in der Praxis sehr problematisch. SVG 2 überlässt es den Browsern, ob sie Stylesheet-Blöcke im externen Dokument verarbeiten, und rät daher davon ab, sich auf
<style>-Regeln in der externen Datei zu verlassen. Präsentationsattribute und Inline-style-Attribute sollten wie gewohnt funktionieren.Es gibt auch einige Probleme bei der Bestimmung von Prozentwerten für Elemente in einer externen Datei, da diese Datei keinen Viewport hat, um die Referenzbreite/-höhe von 100 % zu erstellen. Das ist jedoch nie ein Problem bei der Verwendung von
<symbol>, da das Symbol seinen eigenen Referenzrahmen für die Prozente erstellt.Duplizierter Inhalt, ob aus derselben oder einer externen Datei, kann mit geerbten CSS-Custom-Properties (aka CSS-Variablen) gestylt werden. Siehe Blogbeitrag mit Demos, die bereits in Firefox funktionieren. Die SVG-Parameter, die Sie in einer URL-Funktion angeben würden, würden CSS-Custom-Properties zugeordnet, die vom Root-Element geerbt werden. Für
<use>-Elemente würden Sie sie einfach mit CSS auf das<use>setzen, und der duplizierte Inhalt würde sie erben.Wenn eines der Symbole den Gradienten aus derselben Datei referenziert und diese Datei als externe Referenz verwendet wird, dann wird der Gradient in Chrome nicht angezeigt, dasselbe gilt für andere Referenzen (z. B. Filter)
Habe dieses Problem in meinem Projekt: https://github.com/w0rm/gulp-svgstore/issues/56