Comment styliser SVG avec CSS externe?

102

J'ai plusieurs graphiques SVG dont je voudrais modifier les couleurs via mes feuilles de style externes - pas directement dans chaque fichier SVG. Je ne mets pas les graphiques en ligne, mais je les stocke dans mon dossier d'images et je les pointe.

Je les ai implémentés de cette manière pour permettre aux info-bulles de fonctionner, et j'ai également enveloppé chacune dans une <a>balise pour permettre un lien.

<a href='http://youtube.com/...' target='_blank'><img class='socIcon' src='images/socYouTube.svg' title='View my videos on YouTube' alt='YouTube' /></a>

Et voici le code du graphique SVG:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
<g>
    <path d="M28.44......./>
</g>
</svg>

J'ai mis ce qui suit dans mon fichier CSS externe (main.css):

.socIcon g {fill:red;}

Pourtant, cela n'a aucun effet sur le graphisme. J'ai également essayé .socIcon g path {} et .socIcon path {}.

Quelque chose ne va pas, peut-être que mon implémentation n'autorise pas les modifications CSS externes ou j'ai raté une étape? J'apprécierais vraiment votre aide! J'ai juste besoin de la possibilité de modifier les couleurs du graphique SVG via ma feuille de style externe, mais je ne peux pas perdre la fonction d'info-bulle et de lien. (Je pourrais peut-être vivre sans info-bulles.) Merci!

Jordanie H
la source
Essayez svg { fill:red; }ou donnez à votre chemin un nom de classe. Par exemple, <path class="socIcon" d="M28.44 ..... />cela devrait faire l'affaire.
Dwza

Réponses:

92

Votre fichier main.css n'aurait d'effet sur le contenu du SVG que si le fichier SVG est inclus en ligne dans le HTML:

https://developer.mozilla.org/en/docs/SVG_In_HTML_Introduction

<html>
  <body>
  <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
      <path d="M28.44......./>
    </g>
  </svg>
</html>

Si vous souhaitez conserver votre SVG dans des fichiers, le CSS doit être défini à l'intérieur du fichier SVG.

Vous pouvez le faire avec une balise de style:

http://www.w3.org/TR/SVG/styling.html#StyleElementExample

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
     width="50px" height="50px" viewBox="0 0 50 50">
  <defs>
    <style type="text/css"><![CDATA[
      .socIcon g {
        fill:red;
      }
    ]]></style>
  </defs>
  <g>
    <path d="M28.44......./>
  </g>
</svg>

Vous pouvez utiliser un outil côté serveur pour mettre à jour la balise de style en fonction du style actif. En rubis, vous pouvez y parvenir avec Nokogiri. SVG est juste du XML. Il existe donc probablement de nombreuses bibliothèques XML disponibles qui peuvent probablement y parvenir.

Si vous ne pouvez pas faire cela, vous devrez simplement les utiliser comme s'il s'agissait de PNG; créer un ensemble pour chaque style et enregistrer leurs styles en ligne.

RVB
la source
17
Cela signifie-t-il qu'il n'existe aucune méthode pour bénéficier de la mise en cache du SVG et de l'application de styles variés? Inline ne semble pas bien mettre en cache, tandis que d'autres méthodes nécessiteraient la création de plusieurs versions de l'image, éliminant tout avantage de leur mise en cache.
msg45f
Une autre façon est d'encoder les SVG en tant qu'uris de données d'image d'arrière-plan, avec des couleurs différentes sur chaque version et de s'appuyer sur gzip pour réduire la taille du fichier en raison de la duplication.
Ruskin
1
Dans mon cas, je voulais remplacer les styles d'élément du SVG. Mon CSS ne fonctionnait pas, car les styles d'élément avaient une priorité plus élevée. La solution la plus simple était d'ajouter un! Important au style CSS pour le SVG. Ensuite, tout allait bien. Si vous voulez éviter! Important, vous devez déplacer les styles d'élément dans le CSS.
David Gausmann le
dommage que vous ne puissiez pas charger une feuille de style dans un svg à partir d'une URL
clayRay
1
@clayRay, vous pourrez le faire de cette façon une fois que SVG2 sera terminé brouillon w3.org/TR/SVG2/styling.html#LinkElement
RVB
51

Vous pouvez faire ce que vous voulez, avec une mise en garde (importante): les chemins dans votre symbole ne peuvent pas être stylés indépendamment via CSS externe - vous ne pouvez définir les propriétés que pour le symbole entier avec cette méthode. Donc, si vous avez deux chemins dans votre symbole et que vous voulez qu'ils aient des couleurs de remplissage différentes, cela ne fonctionnera pas, mais si vous voulez que tous vos chemins soient identiques, cela devrait fonctionner.

Dans votre fichier html, vous voulez quelque chose comme ceci:

<style>
  .fill-red { fill: red; }
  .fill-blue { fill: blue; }
</style>

<a href="//www.example.com/">
  <svg class="fill-red">
    <use xlink:href="images/icons.svg#example"></use>
  </svg>
</a>

Et dans le fichier SVG externe, vous voulez quelque chose comme ceci:

<svg xmlns="http://www.w3.org/2000/svg">
   <symbol id="example" viewBox="0 0 256 256">
    <path d="M120.... />
  </symbol>
</svg>

Échangez la classe sur la svgbalise (dans votre html) de fill-redà fill-blueet ta-da ... vous avez du bleu au lieu de rouge.

Vous pouvez partiellement contourner la limitation de pouvoir cibler les chemins séparément avec du CSS externe en mélangeant et en faisant correspondre le CSS externe avec du CSS en ligne sur des chemins spécifiques, car le CSS en ligne aura la priorité. Cette approche fonctionnerait si vous faites quelque chose comme une icône blanche sur un fond coloré, où vous souhaitez changer la couleur de l'arrière-plan via le CSS externe mais l'icône elle-même est toujours blanche (ou vice-versa). Donc, avec le même HTML qu'avant et quelque chose comme ce code svg, vous obtiendrez un fond rouge et un chemin de premier plan blanc:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="example" viewBox="0 0 256 256">
    <path class="background" d="M120..." />
    <path class="icon" style="fill: white;" d="M20..." />
  </symbol>
</svg>
Adam Korman
la source
bonne réponse .. je pense que la mise en garde devrait vraiment être: le support du navigateur, cependant! bonne référence (plus de détails que caniuse): css-tricks.com/svg-fragment-identifiers-work
ptim
C'est une solution! En fait, il n'est pas nécessaire d'envelopper tout le contenu svg dans un symbolélément, c'est-à-dire que vous pouvez simplement donner un idà l'élément svg, donc: `<svg id = example" xmlns = " w3.org/2000/svg " viewBox = "0 0 256 256 "> <path class =" background "d =" M120 ... "/> <path class =" icon "style =" fill: white; "d =" M20 ... "/> </ svg > `
SimoneMSR
12

Vous pouvez inclure dans vos fichiers SVG un lien vers un fichier css externe en utilisant:

<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>

Vous devez mettre ceci après l'ouverture de la balise:

<svg>
  <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>
  <g>
    <path d=.../>
  </g>
</svg>

Ce n'est pas la solution parfaite, car vous devez modifier les fichiers svg, mais vous les modifiez une fois et que tous les changements de style peuvent être effectués dans un fichier css pour tous les fichiers svg.

Jacek Mamot
la source
1
Wow, cela fonctionne, mais seulement 1 vote positif ... cette solution est-elle bonne pour toutes les situations? C'est si simple, pourquoi n'est-ce pas la réponse choisie?
Bruno Vincent le
Le but est de centraliser vos définitions de style. Disons que vous avez 10 SVG que vous souhaitez styliser. Vous devez maintenant copier une référence au CSS dans tous les SVG qui doivent être affectés. Et si le nom de fichier / l'emplacement de votre CSS change, vous devez le mettre à jour en 10 SVG. Une classe CSS semble beaucoup plus symbolique qu'une référence à un fichier CSS physique.
Frans
Notez que svg chargé via une balise <img> ne chargera pas de contenu externe (par exemple, les feuilles de style).
Moose Morals
7

Il est possible de styliser un SVG en créant dynamiquement un élément de style en JavaScript et en l'ajoutant à l'élément SVG. Hacky, mais ça marche.

<object id="dynamic-svg" type="image/svg+xml" data="your-svg.svg">
    Your browser does not support SVG
</object>
<script>
    var svgHolder = document.querySelector('object#dynamic-svg');
    svgHolder.onload = function () {
        var svgDocument = svgHolder.contentDocument;
        var style = svgDocument.createElementNS("http://www.w3.org/2000/svg", "style");

        // Now (ab)use the @import directive to load make the browser load our css
        style.textContent = '@import url("/css/your-dynamic-css.css");';

        var svgElem = svgDocument.querySelector('svg');
        svgElem.insertBefore(style, svgElem.firstChild);
    };
</script>

Vous pouvez générer le JavaScript dynamiquement en PHP si vous le souhaitez - le fait que cela soit possible en JavaScript ouvre une myriade de possibilités.

Pete
la source
hé j'aime vraiment votre solution et cela fonctionne mais je dois l'adopter sur ma situation si vous êtes prêt à aider bien sûr, j'ai à l'intérieur <defs> une balise de style que je peux les supprimer manuellement et exécuter ce code pour qu'il crée un style, existe-t-il un moyen de supprimer les définitions, puis de recréer l'élément comme vous l'avez fait ou simplement de le mettre à jour, et l'URL a également une erreur URL n'est pas définie pouvez-vous s'il vous plaît aider, merci
Kamel Mili
6

Une approche que vous pouvez adopter consiste simplement à utiliser des filtres CSS pour modifier l'apparence des graphiques SVG dans le navigateur.

Par exemple, si vous avez un graphique SVG qui utilise une couleur de remplissage rouge dans le code SVG, vous pouvez le rendre violet avec un paramètre de rotation de teinte de 180 degrés:

#theIdOfTheImgTagWithTheSVGInIt {
    filter: hue-rotate(180deg);
    -webkit-filter: hue-rotate(180deg);
    -moz-filter: hue-rotate(180deg);
    -o-filter: hue-rotate(180deg);
    -ms-filter: hue-rotate(180deg);
}

Expérimentez avec d'autres paramètres de rotation de teinte pour trouver les couleurs souhaitées.

Pour être clair, le CSS ci-dessus va dans le CSS qui est appliqué à votre document HTML. Vous stylisez la balise img dans le code HTML, pas le code du SVG.

Et notez que cela ne fonctionnera pas avec les graphiques remplis de noir, de blanc ou de gris. Vous devez avoir une couleur réelle pour faire pivoter la teinte de cette couleur.

Simon White
la source
4

Une solution très rapide pour avoir un style dynamique avec une feuille de style css externe, au cas où vous utiliseriez la <object>balise pour incorporer votre svg.

Cet exemple ajoutera une classe à la <svg>balise racine en cliquant sur un élément parent.

file.svg:

<?xml-stylesheet type="text/css" href="../svg.css"?>
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="">
  <g>
   <path/>
  </g>
 </svg>

html:

<a class="parent">
  <object data="file.svg"></object>
</a>

Jquery:

$(function() {
  $(document).on('click', '.parent', function(){
    $(this).find('object').contents().find('svg').attr("class","selected");
  }
});

sur l'élément parent click:

 <svg xmlns="http://www.w3.org/2000/svg" viewBox="" class="selected">

alors vous pouvez gérer votre css

svg.css:

path {
 fill:none;
 stroke:#000;
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}

.selected path {
 fill:none;
 stroke:rgb(64, 136, 209);
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}
vhanahrni
la source
ne semble pas fonctionner, pourriez-vous ajouter un exemple fonctionnel?
Jeanluca Scaljeri
4

Cela devrait être possible en insérant d'abord les images svg externes. Le code ci-dessous vient de remplacer toutes les images SVG par SVG en ligne par Jess Frazelle.

$('img.svg').each(function(){
  var $img = $(this);
  var imgID = $img.attr('id');
  var imgClass = $img.attr('class');
  var imgURL = $img.attr('src');
  $.get(imgURL, function(data) {
    // Get the SVG tag, ignore the rest
    var $svg = $(data).find('svg');
    // Add replaced image's ID to the new SVG
    if (typeof imgID !== 'undefined') {
      $svg = $svg.attr('id', imgID);
    }
    // Add replaced image's classes to the new SVG
    if (typeof imgClass !== 'undefined') {
      $svg = $svg.attr('class', imgClass+' replaced-svg');
    }
    // Remove any invalid XML tags as per http:validator.w3.org
    $svg = $svg.removeAttr('xmlns:a');
    // Replace image with new SVG
    $img.replaceWith($svg);
  });
});
Leo
la source
3
il est important de noter que cela ne fonctionnera que si vous avez l'image hébergée sur le même domaine que le html, ou si vous avez une politique interdomaine spécialement configurée sur le serveur d'images. $ .get utilisera ajax et ne parviendra pas à charger l'image à partir du serveur externe s'il n'y a pas d'en-tête d'autorisation d'accès valide
user151496
c'est légendaire
Tino Costa 'El Nino'
2

Lorsqu'il est utilisé dans une <image>balise, le SVG doit être contenu dans un seul fichier pour des raisons de confidentialité. Ce bogue bugzilla a plus de détails sur exactement pourquoi il en est ainsi. Malheureusement, vous ne pouvez pas utiliser une balise différente telle que an <iframe>car cela ne fonctionnera pas comme un lien, vous devrez donc intégrer le CSS dans une <style>balise dans le fichier lui-même.

Une autre façon de faire cela serait d'avoir les données SVG dans le fichier html principal, c'est-à-dire

<a href='http://youtube.com/...' target='_blank'>
  <svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
        <path d="M28.44......./>
    </g>
  </svg>
</a>

Vous pouvez styliser cela avec un fichier CSS externe en utilisant la <link>balise HTML .

Robert Longson
la source
2
Je ne peux pas mettre les styles dans les fichiers. Je vais en fait changer les couleurs de ces images en fonction du jeu de couleurs que l'utilisateur a choisi pour mon site. Mon implémentation actuelle consiste à ajouter une feuille de style à la page principale qui écrase les styles par défaut. Je voulais mettre les changements de couleur dans ce fichier pour affecter les graphiques SVG intégrés. Mais cela ne fonctionnerait pas car je dois créer un lien vers la feuille de style à partir du fichier SVG (à moins qu'il n'y ait un moyen d'ajouter ce lien à tous les fichiers SVG en utilisant également JavaScript). Il existe sûrement un moyen d'autoriser les fichiers SVG externes, les CSS externes, les liens et les info-bulles.
Jordan H
1
Il n'est pas possible de faire ce que vous voulez en raison du modèle de sécurité du navigateur. Vous ne pouvez pas utiliser javascript pour manipuler SVG lorsqu'il est utilisé comme image. Pensez à SVG lorsqu'il est utilisé comme image comme un fichier png ou gif animé, le tout dans un seul fichier et sans accès au script.
Robert Longson
1

"Je vais en fait changer les couleurs de ces images en fonction de la palette de couleurs choisie par l'utilisateur pour mon site." - Jordanie il y a 10 heures

Je vous suggère d'utiliser PHP pour cela. Il n'y a vraiment pas de meilleur moyen de le faire sans les polices d'icônes, et si vous résistez à leur utilisation, vous pouvez essayer ceci:

<?php

    header('Content-Type: image/svg+xml');
    echo '<?xml version="1.0" encoding="utf-8"?>';
    $color = $_GET['color'];

?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
        <path fill="<?php echo $color; ?>" d="M28.44..."/>
    </g>
</svg>

Et plus tard, vous pourrez utiliser ce fichier filename.php?color=#ffffffpour obtenir le fichier svg dans la couleur souhaitée.

SeinopSys
la source
6
Notez que ce code ne vérifie pas l'entrée de l'utilisateur - tout peut être fourni en couleur et votre SVG peut être rendu de manière très intéressante ... Je ne sais pas si cela vous affecte, mais vous devriez prendre l'habitude de TOUJOURS valider l'entrée de l'utilisateur. Quelque chose comme ça aiderait: if (!preg_match('/^[#][0-9a-f]{6}$/i', $_GET['color'])) die('Oops!');(placez-le quelque part dans le bloc PHP de démarrage).
johndodo
1

Ce qui fonctionne pour moi: balise de style avec règle @import

<defs>
    <style type="text/css">
        @import url("svg-common.css");
    </style>
</defs>
Fordi
la source
1

Je sais que c'est un ancien post, mais juste pour résoudre ce problème ... vous utilisez simplement vos classes au mauvais endroit: D

Tout d'abord, vous pouvez utiliser

svg { fill: red; }

dans votre main.css pour le rendre rouge. Cela a un effet. Vous pouvez également utiliser des sélecteurs de nœuds pour obtenir des chemins spécifiques.

La deuxième chose est que vous avez déclaré la classe au img-Tag.

<img class='socIcon'....

Vous devriez en fait le déclarer dans votre SVG. si vous avez des chemins différents, vous pouvez bien sûr en définir plus.

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
<g>
    <path class="myClassForMyPath" d="M28.44......./>
</g>
</svg>

maintenant vous pouvez changer la couleur de votre main.cssgoût

.myClassForMyPath {
    fill: yellow;
}
Dwza
la source
J'ai essayé cela, cela ne fonctionne pas, comme le disent déjà beaucoup d'autres réponses. Vous ne pouvez pas appliquer de style aux classes à l'intérieur du SVG.
Frans
@Frans incluez-vous un svg en tant que fichier ou avez-vous votre source svg comme dans l'exemple ci-dessus? Parce que j'ai à l'esprit que cela dépend de la façon dont vous utilisez svg. Y compris par img ne fonctionnera pas.
Dwza
Exactement, cela ne fonctionne que si vous insérez le SVG dans votre HTML. Mais ce n'est pas ce que fait votre exemple. Il utilise un SVG externe (c'est-à-dire non en ligne). Il semble qu'il n'y ait aucun moyen de styliser un SVG externe avec CSS dans votre HTML.
Frans
sûr que vous avez essayé mon exemple correct? je veux dire, inclus un fichier css externe? <?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
Dwza
OK, maintenant je vois, vous avez une <?xml-stylesheet ... ?>déclaration dans votre SVG. Je suppose que cela fonctionnerait. C'est similaire à d'autres réponses recommandant un <link rel="stylesheet" ... >à l'intérieur du SVG. Il a également les mêmes problèmes (vous devez mettre à jour chaque SVG pour qu'il pointe vers la feuille de style, et tout changement de nom ou d'emplacement de la feuille de style signifie qu'il faut à nouveau changer tous les SVG).
Frans
1
  1. Pour les styles externes

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">

  <style>
	@import url(main.css);
  </style>

  <g>
    <path d="M28.44......./>
  </g>
</svg>

  1. Pour les styles internes

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">

  <style>
	    .socIcon g {fill:red;}
  </style>

  <g>
    <path d="M28.44......./>
  </g>
</svg>

Remarque: les styles externes ne fonctionneront pas si vous incluez une <img>balise SVG à l'intérieur . Cela fonctionnera parfaitement à l'intérieur de la <div>balise

Yuvraj Patil
la source
0

@leo voici la version angularJS, merci encore

G.directive ( 'imgInlineSvg', function () {

return {
    restrict : 'C',
    scope : true,
    link : function ( scope, elem, attrs ) {

        if ( attrs.src ) {

            $ ( attrs ).each ( function () {
                var imgID    = attrs.class;
                var imgClass = attrs.class;
                var imgURL   = attrs.src;

                $.get ( imgURL, function ( data ) {

                    var $svg = $ ( data ).find ( 'svg' );
                    if ( typeof imgID !== 'undefined' ) {
                        $svg = $svg.attr ( 'id', imgID );
                    }

                    if ( typeof imgClass !== 'undefined' ) {
                        $svg = $svg.attr ( 'class', imgClass + ' replaced-svg' );
                    }

                    $svg = $svg.removeAttr ( 'xmlns:a' );

                    elem.replaceWith ( $svg );

                } );

            } );
        }

    }

}

} );
Tino Costa 'El Nino'
la source
-1

Cette méthode fonctionnera si le svg est affiché dans un navigateur Web, mais dès que ce code est téléchargé sur le serveur et que la classe de l'icône svg est codée comme s'il s'agissait d'une image d'arrière-plan, la couleur est perdue et revient à la couleur par défaut . On dirait que la couleur ne peut pas être modifiée à partir de la feuille de style externe même si la classe svg pour la couleur et la classe de couche supérieure pour l'affichage et la position du svg sont toutes deux mappées dans le même répertoire.

HTMLGUY1999
la source