Home > Software design >  SVG <use> ignores Gradient styling
SVG <use> ignores Gradient styling

Time:11-11

I have a svg file that does not render gradient in the <use> tag. Why is it behaving differently, and how can I fix it?

SVG File:

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100"
    style="stroke:#000000;stroke-width:2"
    xml:space="preserve"
     id="pb_svg_1">
   <rect width="50" height="50" style="fill: url(#lg1)"/>
   <defs>
      <linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
         <stop offset="0%" style="stop-color:rgb(88,88,88);stop-opacity:1"></stop>
         <stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"></stop>
      </linearGradient>
   </defs>
</svg>

These are 3 methods I render the svg. 2 work but the one I need does not:

<!-- Does NOT include gradient style -->
<svg width="100" height="100">
    <use href="12.svg#pb_svg_1" width="100" height="100"/>
</svg>
<!-- Gradient style works! -->
<div>
    <object data="12.svg" width="100" height="100"></object>
</div>
<!-- Gradient style works! -->
<div style="width: 100px;height: 100px">
    <embed src="12.svg"/>
</div>

I expect the use element to render the file as it does when the svg is on the same page.

EDIT: It does work in firefox and does not work in chrome and edge

CodePudding user response:

Workaround: define gadients in an inlined svg

Move the gradient <defs> to an inlined hidden <svg>.
It's important to hide this svg via zero width and height properties like width:0; height:0;position:absolute;.
display:none or visibility:hidden will remove/disable gradients, clip paths etc.

<!-- HTML svg use instance -->
<svg width="100" height="100" viewBox="0 0 100 100">
    <use href="#pb_svg_1" style="fill: url(#lg1); stroke:#000000;stroke-width:2 "/>
</svg>

<!-- Inline svg: hidden gradient definition -->
<svg style="width:0; height:0; position:absolute;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
   <defs>
      <linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
         <stop offset="0%" style="stop-color:rgb(88,88,88);stop-opacity:1"/>
         <stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
      </linearGradient>
   </defs> 
</svg>

<!-- External svg: 12.svg -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <symbol  id="pb_svg_1" viewBox="0 0 100 100">
   <rect x="0" y="0" width="50" height="50" /></symbol>
</svg>

Workaround 2: inline external use references

If refactoring all svg assets isn't feasible – change your embedding method.

2.1 via fetch

HTML

<!-- HTML svg use instance -->
<svg width="100" height="100" viewBox="0 0 100 100">
    <use  href="12.svg#pb_svg_1" />
</svg> 

Js

inlineExternalUse();

function inlineExternalUse(){
    let extSvgs = document.querySelectorAll('use');
    if(extSvgs.length){
        extSvgs.forEach(function(item, i){
            let href = item.getAttribute('href') ? item.getAttribute('href') : item.getAttribute('xlink:href');
            // change href to inline reference
            let hrefNew = '#' href.split('#')[1];
            item.setAttribute('href', hrefNew);
        
            fetch(href)
              .then(response => response.text() )
              .then(data => {
                //inline ext svg
                let parser = new DOMParser();
                let svgInline = parser.parseFromString(data, "application/xml").querySelector('svg');
                svgInline.setAttribute('aria-hidden', 'true')
                svgInline.style.width=0;
                svgInline.style.height=0;
                svgInline.style.position='absolute';
                document.body.appendChild(svgInline);
              });
        });
    }
}

2.2: via native web component

See @Danny '365CSI' Engelman's article "〈load-file〉Web Component, add external content to the DOM" or this answer "How to change the color of an svg element?"

  • Related