CSS-Tricks has an article about
duplicate titles and id
attributes in svg.
The article discusses the problems that might arise when
an author is relying on title
elements and id
attributes for
ARIA
accessibility. But there's another, more fundamental
problem if you insert
svg
code directly in an html document and end up with
with duplicate id
attributes. A problem that could
bork how the browser renders the svg.
Let's begin with a simple example of an html page with svg elements:
<body>
<svg viewBox="0 0 300 200">
<rect width="300" height="200" fill="blue"/>
</svg>
<svg viewBox="0 0 300 200">
<rect width="300" height="200" fill="red"/>
</svg>
</body>
Such a page will display two rectangles, one blue, one red.
Suppose you want the color to shift, from one color on the
left side to another on the right. For that, you
need to create a separate gradient element, give that
element an id
attribute, and reference it in the svg
rectangle with a
fragment url.
Here's an example:
<body>
<svg viewBox="0 0 300 200">
<linearGradient id="myfill">
<stop offset="0" stop-color="blue"/>
<stop offset="1" stop-color="green"/>
</linearGradient>
<rect width="300" height="200" fill="url(#myfill)"/>
</svg>
<svg viewBox="0 0 300 200">
<linearGradient id="myfill">
<stop offset="0" stop-color="red"/>
<stop offset="1" stop-color="yellow"/>
</linearGradient>
<rect width="300" height="200" fill="url(#myfill)"/>
</svg>
</body>
You might expect this page to show two rectangles, one
blue-to-green, the other red-to-yellow. But that's not
what it produces. Take a look at a live version of this
svg gradient example.
Although the svg elements have different
<linearGradient/>
s, the rectangles are identical:
blue-to-green in Firefox 86 and Chrome 88 on Mac OSX;
red-to-yellow in Safari 13.1.2 on Mac OSX as well as
Safari, Firefox, and Chrome on iOS 9.3.6
(yes, my iphone is really old).
what's going on?
The sharp eye will notice that there are two identical
id
attributes in the example code, and that's not
allowed. In an html document,
each id
must be unique,
so the browsers have to do a little error correction.
In Firefox and Chrome on OSX, it appears to go like this:
- get the rectangle's
fill attribute,
which in the example code is
url(#myfill)
- retrieve the
fragment url
#myfill
, that is, search the example document for an element whoseid="myfill"
- find and use the first instance of
<linearGradient id="myfill"/>
(and ignore the second) - render the rectangle blue-to-green as defined in
that first
<linearGradient/>
element
Note that Firefox and Chrome follow the same steps for each rectangle, resulting in both rectangles having a blue-to-green color.
Safari OSX and Safari/Firefox/Chrome iOS follow the same steps 1 and 2 as listed above. Then they
- find and use the second instance of
<linearGradient id="myfill"/>
(and ignore the first) - render the rectangle red-to-yellow as defined in
that second
<linearGradient/>
element.
As before, these browsers follow the same steps for each rectangle, so each rectangle ends up with the same color, only in this case it's red-to-yellow.
solution
There is no way apply a gradient without using the id
attribute:
You must give the gradient an id attribute; otherwise it can't be referenced by other elements inside the file. Gradients are defined in a defs section as opposed to on a shape itself....
But you can change the id
on the second <linearGradient/>
element so that the browser can differentiate between them. And
since documents are not supposed to contain two or more matching
id
attributes, that's the right thing to do anyways.
You could also save the svg elements as separate .svg files
— making the clashing id
problem moot —
and use <img>
elements to put them in your web page.
real world encounter of clashing ids
In ,
I published two versions of the
microformats logo in svg format
(using <img>
elements to put them on the page).
Each version of the logo was in its own .svg file,
and, for no particular reason, I used the
same id
attribute for the <linearGradient/>
element
in both files.
A couple of weeks later, I decided to remove the <img>
elements and put the svg
markup directly in the
page instead. When I did, kaplooey! I got two identical logos.
And since I rarely use the id attribute, I didn't even think
about clashing id
s.
After half-an-hour or so of head scratching, I realized
my mistake and changed the id
and fill
attributes of
the orange logo. Problem solved.