web-dev-qa-db-fra.com

Inclure des fichiers SVG avec du HTML et être toujours en mesure de leur appliquer des styles?

Vous pouvez inclure des fichiers SVG dans votre fichier HTML via les balises embedobject et svg.

  • L'utilisation d'une balise embed ou object nécessite de lier l'image à une URL. (C'est ce que je préfère, parce que je n'aime pas tout ce code SVG dans mon code HTML, donc je voudrais le garder comme ça.)
  • L'utilisation de la balise svg (AFAIK) nécessite d'avoir le code SVG en ligne dans votre code HTML.

Ma question est:

Comment puis-je inclure des icônes SVG, des images et d'autres fichiers dans mon fichier HTML, sans avoir à mettre tout le code SVG dedans, tout en étant en mesure de leur appliquer des styles? Les appliquer via JS est également très bien.

Remarque:

Lorsque je les inclue via object ou embed, je n'arrive pas à y accéder via jQuery, même avec $("#my-svg-div").find("svg") (qui, soit dit en passant, presque toutes les réponses sur SO dit que je devrais). Je reçois juste undefined.

Je vous remercie!

15
Axinite

Réponse courte

Vous pouvez programmer une image SVG en ligne. Une telle image peut être traitée essentiellement de manière identique à un élément <svg> En ligne réel, y compris la possibilité de lui appliquer des styles.

Si votre image SVG est référencée dans un élément <object> Ou <iframe> (e), vous pouvez l'intégrer comme suit:

e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true), e);

Si votre image SVG est référencée dans un élément <embed>, Remplacez .contentDocument Dans le code ci-dessus par .getSVGDocument().

Si votre image SVG est référencée dans un élément <img>, Une stratégie complètement différente, impliquant AJAX et décrite ci-dessous, peut être utilisée pour aligner l'image.

Stratégie générale

Si vos fichiers d'image SVG externes sont de même origine (par exemple, les images sont chargées au même endroit que le code HTML), alors une approche qui permet de styliser ces images est de les aligner par programme comme suit:

  • Récupérez le contenu du fichier SVG externe.
  • Ajoutez ce contenu directement à votre fichier HTML dans le même emplacement HTML DOM que l'élément de référence d'origine, c'est-à-dire mettez-le "en ligne".
  • Supprimez l'élément qui faisait à l'origine référence au fichier SVG externe.

Avantages

Cette stratégie intégrée vous offre le meilleur des deux mondes:

  1. Vous bénéficiez des avantages de fichiers image séparés, notamment:

    • organiser vos fichiers image indépendamment du HTML,
    • garder votre fichier HTML d'origine à l'abri des détails de l'image, et
    • (potentiellement) permettant au navigateur de mettre en cache les images (mais voir ci-dessous concernant ce dernier point).
  2. Pourtant, vous pouvez toujours faire quoi que ce soit aux images SVG éventuellement intégrées que vous pourriez faire aux éléments <svg> Qui étaient vraiment à l'origine en ligne, y compris:

    • leur appliquer des styles CSS,
    • appliquer des écouteurs d'événements à des formes ou groupes SVG individuels, etc.

La mise en oeuvre

Pour les éléments <object> Ou <iframe>:

Vous pouvez incorporer du code SVG référencé en externe comme suit:

// using Vanilla JavaScript (as shown above):
e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true), e);

// using jQuery:
$e.replaceWith($($e[0].contentDocument.documentElement).clone());

... où e ou $e sont les variables Vanilla ou jQuery (respectivement) dans lesquelles vous avez sélectionné un référencement SVG externe <object> ou <iframe> élément.

Pour inclure des éléments <embed>:

Si, à la place, vous utilisez un élément de référence externe-SVG <embed>, Vous pouvez incorporer le contenu SVG en remplaçant .contentDocument Dans le code ci-dessus par .getSVGDocument() (note les parenthèses supplémentaires). Notez que .contentDocument Ne fonctionne pas avec les éléments <embed> Tandis que .getSVGDocument() fonctionne réellement avec les trois types d'élément. Cependant .getSVGDocument()est déconseillé et ne doit donc être utilisé que si vous avez vraiment besoin d'éléments <embed>.

Pour inclure des éléments <img>:

Aucune des stratégies ci-dessus ne fonctionne pour les éléments <img>. Pour les intégrer, vous pouvez récupérer l'attribut src de l'élément <img>, Faire une demande AJAX pour ce fichier, créer un nouvel élément <svg> En utilisant le code SVG récupéré et remplacez l'élément <img> d'origine par le nouvel élément <svg>. Si vous souhaitez que cette stratégie fonctionne pour les quatre types d'éléments, sachez simplement que l'URL de l'image SVG référencée est conservée dans l'attribut src de <iframe>, <embed> et les éléments <img> mais dans l'attribut data d'un élément <object>. Cette stratégie peut être mise en œuvre comme suit:

// using Vanilla JavaScript:
var xhr = new XMLHttpRequest();
xhr.open("GET", e.getAttribute(e.nodeName === "OBJECT" ? "data" : "src");
xhr.send();
xhr.onreadystatechange = function() {
  if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
    e.outerHTML = xhr.responseText;
  }
};

// using jQuery:
$.get($e.attr($e.prop("nodeName") === "OBJECT" ? "data" : "src"), function(data) {
  $e.replaceWith(data.documentElement);
});

Exemple

L'exemple suivant montre où la stratégie ci-dessus permet et ne permet pas d'appliquer des styles CSS externes aux images SVG à l'origine externes. (Je n'ai pas créé d'extrait de code ou de jsfiddle en raison de la nécessité de référencer des fichiers externes locaux.)

Les deux captures d'écran suivantes montrent le style CSS (triangles rouges) ou son absence (triangles noirs) avant et après l'inlining. Il affiche les résultats des images SVG initialement intégrées dans le code HTML (<svg>) Ou référencées dans les éléments indiqués (<object>, <iframe>, <embed> Et <img>). Les trois lignes montrent les résultats de l'incrustation en utilisant les trois stratégies indiquées.

Avant de cliquer sur le bouton, aucune inlining n'a encore été tentée et l'écran ressemble à ceci. Seuls les éléments SVG intégrés (la 1ère colonne) sont stylisés:

svg polygon styling by CSS before svg inlining

Après avoir cliqué sur le bouton, l'inline est tenté et l'écran ressemble maintenant à ceci. Le style CSS a été appliqué avec succès à certains des éléments:

svg polygon styling by CSS after svg inlining

Le code requis pour cet exemple est le suivant:

image.svg (fichier référencé en externe, c'est-à-dire non incorporé dans le HTML):

<svg xmlns="http://www.w3.org/2000/svg" width="50px" height="50px">
  <polygon points="25,5 45,45 5,45 25,5"/>
</svg>

index.html (évidemment, supprimez la ligne de script jQuery si vous n'utilisez pas jQuery):

<!DOCTYPE html>
  <head>
    <link href="styles.css" rel="stylesheet">
    <script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
    <script src="main.js"></script>
  </head>
<body>
  <button>Click to attempt to inline svg images.</button>
  <table>
    <tr>
      <th></th>
      <th>svg   </th>
      <th>object</th>
      <th>iframe</th>
      <th>embed </th>
      <th>img   </th>
    </tr>
    <tr>
      <td>contentDocument</td>
      <td><svg  xmlns="http://www.w3.org/2000/svg" width="50" height="50"><polygon points="25,5 45,45 5,45 25,5"/></svg></td>
      <td><object data="image.svg" type="image/svg+xml"></object></td>
      <td><iframe  src="image.svg" width="50" height="50" style="border: none;"></iframe></td>
      <td><embed   src="image.svg" type="image/svg+xml" /></td>
      <td><img     src="image.svg" /></td>
    </tr>
    <tr>
      <td>getSVGDocument()<br />(deprecated)</td>
      <td><svg  xmlns="http://www.w3.org/2000/svg" width="50" height="50"><polygon points="25,5 45,45 5,45 25,5"/></svg></td>
      <td><object data="image.svg" type="image/svg+xml"></object></td>
      <td><iframe  src="image.svg" width="50" height="50" style="border: none;"></iframe></td>
      <td><embed   src="image.svg" type="image/svg+xml" /></td>
      <td><img     src="image.svg" /></td>
    </tr>
    <tr>
      <td>XMLHttpRequest</td>
      <td><svg  xmlns="http://www.w3.org/2000/svg" width="50" height="50"><polygon points="25,5 45,45 5,45 25,5"/></svg></td>
      <td><object data="image.svg" type="image/svg+xml"></object></td>
      <td><iframe  src="image.svg" width="50" height="50" style="border: none;"></iframe></td>
      <td><embed   src="image.svg" type="image/svg+xml" /></td>
      <td><img     src="image.svg" /></td>
    </tr>
  </table>
</body>
</html>

styles.css (seule la ligne polygon est importante pour démontrer l'inline):

polygon {fill: red;}
table {border-collapse: collapse;}
td, th {border: solid black 1px; padding: 0.4em;}

main.js (version jQuery):

$(document).ready(function() {
  $("button").click(function() {
    ["object", "iframe", "embed", "img"].forEach(function(elmtType) {
      var $e, $threeElmts = $(elmtType);
      $e = $($threeElmts[0]);
      if ($e[0].contentDocument) $e.replaceWith($($e[0].contentDocument.documentElement).clone());
      $e = $($threeElmts[1]);
      if ($e[0].getSVGDocument) $e.replaceWith($($e[0].getSVGDocument().documentElement).clone());
      $e = $($threeElmts[2]);
      $.get($e.attr($e.prop("nodeName") === "OBJECT" ? "data" : "src"), function(data) {
        $e.replaceWith(data.documentElement);
      });
    });
  });
});

main.js (version JavaScript vanille):

document.addEventListener("DOMContentLoaded", function() {
  document.querySelector("button").addEventListener("click", function() {
    ["object", "iframe", "embed", "img"].forEach(function(elmtType) {
      var e, threeElmts = document.querySelectorAll(elmtType);
      e = threeElmts[0];
      if (e.contentDocument) e.parentElement.replaceChild(e.contentDocument.documentElement.cloneNode(true), e);
      e = threeElmts[1];
      if (e.getSVGDocument) e.parentElement.replaceChild(e.getSVGDocument().documentElement.cloneNode(true), e);
      e = threeElmts[2];
      var xhr = new XMLHttpRequest();
      xhr.open("GET", e.getAttribute(e.nodeName === "OBJECT" ? "data" : "src"));
      xhr.send();
      xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) e.outerHTML = xhr.responseText;
      };
    });
  });
});

Notez les points suivants:

  • Cette stratégie vous obligera à gérer toutes les caractéristiques associées aux éléments de référencement d'origine, par exemple images de secours, attributs id et class (ou tout autre), écouteurs d'événements, fonctionnalité iframe, etc.
  • Assurez-vous de n'essayer d'inliner qu'après le chargement réel du fichier image.
  • Pensez à l'inliner côté serveur pour permettre la diffusion d'un seul fichier, mais à l'inliner côté client pour autoriser la mise en cache du fichier image.
  • J'ai vérifié cette stratégie dans Firefox 44.0, Chrome 49.0 et Opera 35.0 (Mac et Windows), Safari 9.0 (Mac) et IE 11 (Windows). Je n'ai pas vérifié Edge ni aucun navigateur mobile.
  • Cette solution traite du référencement des fichiers SVG à partir des éléments object, iframe, embed et img. Il ne traite pas des images SVG externes référencées dans les propriétés CSS background-image Ou dans la fonction drawImage d'un contexte <canvas>. Je soupçonne que cela ne fonctionnera pas de cette façon, mais je n'ai pas vérifié.
  • Si vous essayez de dupliquer l'exemple de code sur votre ordinateur local en copiant et en enregistrant le code, puis en "ouvrant" ing index.html, Chrome et Opera ont des paramètres de sécurité cela rendra cela impossible. Pour que ces navigateurs fonctionnent dans une telle configuration locale, vous devez plutôt exécuter un serveur local comme indiqué dans cette autre SO question . Il ne devrait pas y avoir de problème si vous utilisez la stratégie d'inline dans un site Web hébergé par un serveur normal.
22
Andrew Willems

Il s'agit de la procédure pas à pas la plus complète (1) des différentes façons d'utiliser les SVG dans votre HTML et (2) de la manière dont vous pouvez faire le style des pièces individuelles (c.-à-d. Les chemins) du SVG via CSS/JS.

https://css-tricks.com/using-svg/

  1. Dans une balise d'image (par exemple: <img src="picture.svg" /> ou comme image d'arrière-plan en CSS = pas de style

  2. Inline - style loin, mais il encombrera votre HTML. PHP aide ici, ou vous pouvez utiliser une tâche de compilation gulp ou quelque chose pour empêcher les SVG d'encombrer votre code de travail, tout en restant en ligne à la fin.

  3. En tant qu'objet, vous pouvez maintenant ajouter CSS dans le fichier .svg:

    <svg ...>
      <style>
        /* SVG specific fancy CSS styling here */
      </style>
      ...
    </svg>
    

    ou

    <?xml-stylesheet type="text/css" href="svg.css" ?>
    
  4. URI de données - peut être idéal pour les images d'arrière-plan. Pas de style.

3
tayvano