Styling the same SVG <object> different ways

26,197

Solution 1

See https://css-tricks.com/using-svg/, section “Using SVG as an <object>”:

[…] if you want the CSS stuff to work, you can't use an external stylesheet or <style> on the document, you need to use a <style> element inside the SVG file itself.

So it seems that it is not possible to style SVG elements inside an object from “outside” the object via CSS.

Solution 2

As CBroe says, its an issue with styling an external object. You can access it via JS and change it, but I doubt thats ideal and there's issues of making sure its loaded first etc.

However, I'm not convinced this is necessarily the best method as you say, unless there are some other requirements (eg no javascript, or libs and it must be external, you can still load it via Snap load method for example then though if you support js).

You can simply use a defs/use statement. I've just used a circle for brevity, but you could have a more complex path or whatever in there.

jsfiddle

<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400">
  <defs>
    <style>
      .svg-circle {
        fill-rule: evenodd;
        fill: 'red';
      }
    </style>
    <circle id="myDefsCircle" class="svg-circle" r="20" cx="100" cy="100"/>
  </defs>

   <use x="10" y="0"   xlink:href="#myDefsCircle" style="fill:red"/>
   <use x="10" y="50"  xlink:href="#myDefsCircle" style="fill:blue"/>
   <use x="10" y="100" xlink:href="#myDefsCircle" style="fill:green"/>
</svg>

Solution 3

I was in the same predicament, but realized it's simply not possible per the current spec because SVG documents exist in their own DOM separate from the main document, similar to iframes (see this answer). However, that doesn't mean we can't hack our way around this limitation for the time being.

Since SVG files are plain text files, I figured why not just render it using JavaScript (being that the question did not explicitly state that JS cannot be used). Using the SVG circle above as an example, the function would look like this:

// Render an SVG circle
// optional oStyles = { selector: style [, ...] }
function renderCircle(oStyles) {
  var sId = ('svg-'+performance.now()).replace('.', ''),
    sCss = '',
    sSel;
  if (!oStyles) oStyles = {};
  for (var i in oStyles) {
    // Handle group of selectors
    sSel = '';
    i.split(/ *, */).forEach(function(s) {
      sSel += '#' + sId + ' ' + s + ',';
    });
    sSel = sSel.slice(0, -1);
    sCss += sSel + '{' + oStyles[i] + '}';
  }
  return '' +
    '<svg xmlns="http://www.w3.org/2000/svg" id="' + sId + '" width="40" height="40" viewBox="0 0 40 40">' +
      '<style type="text/css">' +
      '<![CDATA[' +
      // Default styles
      '#' + sId + ' .svg-circle { fill: red; }' +
      // Overrides
      sCss +
      ']]>' +
      '</style>' +
      '<circle class="svg-circle" r="20" cx="20" cy="20"/>' +
    '</svg>';
}

document.getElementById('canvas').innerHTML = renderCircle();
document.getElementById('canvas').innerHTML += renderCircle({'.svg-circle':'fill:blue'});
<div id="canvas"></div>

This works okay for a one-off image like a logo, but if you have a bunch of SVG icons, then you should consider using an SVG icon font or SVG sprites. Here's a good guide for implementing SVGs for the web in general:

https://svgontheweb.com/

Share:
26,197
Admin
Author by

Admin

Updated on August 12, 2022

Comments

  • Admin
    Admin almost 2 years

    I want to have a series of the same SVG file on a page with different colours. I'm aware that the best method of getting the SVG into the page without bloating the code, and still have it externally stylable, is through the <object> tag.

    Here's what I have so far:

    HTML

    <object type="image/svg+xml" data="images/circle.svg" class="object-circle red" >
        <!-- fallback image in CSS -->
    </object>
    
    <object type="image/svg+xml" data="images/circle.svg" class="object-circle blue" >
        <!-- fallback image in CSS -->
    </object>
    

    CSS

    .object-circle {
        height:16px;
        width:16px;
    }
    
    .red .svg-circle {
        fill:#f00;
    }
    .blue .svg-circle {
        fill:#00f;
    }
    

    SVG

    <?xml-stylesheet type="text/css" href="styles.css" ?>
    <svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400">
      <defs>
        <style>
          .svg-circle {
            fill-rule: evenodd;
          }
        </style>
      </defs>
      <path class="svg-circle" d="M200,398.688A199.552,199. ..."/>
    </svg>
    

    This just doesn't work as is. I believe there's an issue with targeting the <object> tag to manipulate the SVG's fill property in the CSS.

    Taking the .red selector off the style sheet and leaving the .svg-circle selector in place works as expected - turning the circle red, but I want to be able to have several on the page with different colours.

    Any help much appreciated!

    If I can't crack this I might just resort to an old-fashioned .png sprite sheet.