How to handle duplicate IDs for inline SVGs?

Viewed 2957

I am using the same SVG multiple times on a webpage. The SVGs are inserted on the server side with PHP like this:

<?php echo file_get_contents("myimage.svg"); ?>

The SVG contains a gradient, which should have different colors on different instances of the SVG.

An HTML document delivered by the server may look like the following snippet. The same SVG has been inserted twice:

#image1 .stop1 { stop-color: #FDF39C }
#image1 .stop2 { stop-color: #FE8A77 }
#image2 .stop1 { stop-color: #64E8EA }
#image2 .stop2 { stop-color: #A79CFC }
<div id="image1">
  <svg width="256" height="256" viewBox="0 0 256 256">
    <defs>
      <linearGradient id="gradient1">
        <stop class="stop1" offset="0%"/>
        <stop class="stop2" offset="100%"/>
      </linearGradient>
    </defs>
    <circle fill="url(#gradient1)" cx="128" cy="128" r="100" />
  </svg>
</div>
<div id="image2">
  <svg width="256" height="256" viewBox="0 0 256 256">
    <defs>
      <linearGradient id="gradient1">
        <stop class="stop1" offset="0%"/>
        <stop class="stop2" offset="100%"/>
      </linearGradient>
    </defs>
    <circle fill="url(#gradient1)" cx="128" cy="128" r="100" />
  </svg>
</div>

The problem is that both instances of the SVG display the gradient defined in the first SVG. Here is a comparison of the actual vs the intended result:

enter image description here

The reason for this is that the gradients of both inline SVGs have the same ID gradient1, because the same SVG which contains that ID is inserted twice on the server. The circle elements' fill is set to url(#gradient1) in both cases, which the browser simply links to the first occurence of that ID, which is the gradient definition in the first SVG. This behavior is correct, but for my case this is a problem.

The question is: How do I avoid duplicate ids? I worked with SVG injection using SVGInject, and this solved the problem by making the IDs unique. SVGInject simply adds a random string to the end of IDs, for example changing gradient1 to gradient1-h4w7xo82.

However, I do not see a solution when inserting the SVGs with PHP. Can you help?

3 Answers

Convert the svg file into php so you can pass the gradientId parameter into it.

<?php
  header('Content-Type: image/svg+xml');

  echo '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
    <defs>
      <linearGradient id="' . $gradientId . '">
        <stop class="stop1" offset="0%"/>
        <stop class="stop2" offset="100%"/>
      </linearGradient>
    </defs>
    <circle fill="url(#' . $gradientId . ')" cx="128" cy="128" r="100" />
  </svg>';
?>

CSS Based Solution I was having the same problem.

The solution to the above problem is, change the css class, so that different css class is targeting different object.

Change -

  1. CSS Class of 2nd element, I have change it to #image2 .stop1-df, appended additional "df"
  2. Linear Gradient ID - of second element - gradient1-df
  3. Circle fill url - #gradient1-df

#image1 .stop1 { stop-color: #FDF39C }
#image1 .stop2 { stop-color: #FE8A77 }
#image2 .stop1-df { stop-color: #64E8EA }
#image2 .stop2-df { stop-color: #A79CFC }
<div id="image1">
  <svg width="256" height="256" viewBox="0 0 256 256">
    <defs>
      <linearGradient id="gradient1">
        <stop class="stop1" offset="0%"/>
        <stop class="stop2" offset="100%"/>
      </linearGradient>
    </defs>
    <circle fill="url(#gradient1)" cx="128" cy="128" r="100" />
  </svg>
</div>
<div id="image2">
  <svg width="256" height="256" viewBox="0 0 256 256">
    <defs>
      <linearGradient id="gradient1-df">
        <stop class="stop1-df" offset="0%"/>
        <stop class="stop2-df" offset="100%"/>
      </linearGradient>
    </defs>
    <circle fill="url(#gradient1-df)" cx="128" cy="128" r="100" />
  </svg>
</div>

If you use https://jakearchibald.github.io/svgomg/ it will convert all styles to URL-parameters, like this: id="a" and url("a"). If you want multiple SVGs on one page, they interfere with eachother. I needed to have several SVGs on the same page, so I made a small util for my self. It will replace those a/b/c urls with a parameter you can insert for your self, so you can have 1 SVG with one_a, one_b etc and the second SVG with second_a, second_b etc. It uses regex, obviously:

# replace id="a" with id="test_a"
removeStr = "id=\"#{strChar}\""
regex =  new RegExp(removeStr,'g')
strInput = strInput.replace(regex,"id=\"#{strName}_#{strChar}\"")        

You can find it here: https://codepen.io/pwiegers/pen/YzreGGo

It saved me a ton of time; it might save you some :-)

Related