2

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

ScottNL
  • 73
  • 5
  • Did you try this with the `file://` protocol? Browsers may block access, I got the error message "Security Error: Content at file:///.../test.html may not load data from file:///.../12.svg", but it worked as soon as I used `http:` – ccprog Nov 02 '22 at 18:12
  • He @ccprog this does not change anything on my side. Tryed it in chrome and edge. https://localhost:8000/12.svg#pb_svg_1 – ScottNL Nov 02 '22 at 18:30
  • 1
    Only graphical elements work with external use as you've seen. – Robert Longson Nov 02 '22 at 18:53
  • It's tricky in terms of cross browser compatibility. This related question might be interesting: [SVG with from external symbol sheet not loading in Firefox or Safari](https://stackoverflow.com/questions/74128326/svg-with-defs-from-external-symbol-sheet-not-loading-in-firefox-or-safari/74173265#74173265). TL;DR: you might define gradients in an inlined svg asset. – herrstrietzel Nov 02 '22 at 23:55
  • He @herrstrietzel. Thanks for that but i cannot find a solution with that. I thought i would try it on firefox and guess what it works. For some reason it does not in chrome and edge. – ScottNL Nov 03 '22 at 08:47

1 Answers1

3

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?"

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
  • He @herrstrietzel. Thanks for the workaround. I read your answer in this post https://stackoverflow.com/questions/74128326/svg-with-defs-from-external-symbol-sheet-not-loading-in-firefox-or-safari/74173265#74173265 and i have tested it and it works. The problem on is i would have to do this for all svg images in my application witch wil be about a thousend when it's done. If no one else has a alternative i will accept this as the answer. I will have to come up with some javascript to do this automatically or look for a different vector base image type. – ScottNL Nov 07 '22 at 06:34