1

Take the following source and save it as a test.svg file.

<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="myGradient:1234" gradientTransform="rotate(90)">
      <stop offset="5%" stop-color="gold"></stop>
      <stop offset="95%" stop-color="red"></stop>
    </linearGradient>
  </defs>

  <circle cx="5" cy="5" r="4" fill="url('#myGradient:1234')"></circle>
</svg>

Open the test.svg file in Chrome and you'll see a circle which blends from yellow to red. However, if you create an HTML file and include this SVG in the form of <img src="text.svg" /> you'll see that, at least in Chrome, no color is applied to the circle.

If you update test.svg to change the ID from myGradient:1234 to myGradient then the color will start working when the image is used in an <img> tag.

Why is this behavior different?

Tyler V.
  • 2,471
  • 21
  • 44

1 Answers1

4

Actually ID's containing ':'(colon) shouldn't work in a self contained file either. It's a matter of XML naming compliance.

An excerpt from the MDN Docs

It must be valid in XML documents. A stand-alone SVG document uses XML 1.0 syntax, which specifies that valid IDs only include designated characters (letters, digits, and a few punctuation marks), and do not start with a digit, a full stop (.) character, or a hyphen-minus (-) character.

As a rule of thumb:
apply the same naming rules, you would also need for html/css/js selectors:

  1. keep it ansi: avoid non-english characters like diacritics/accents etc.
  2. don't start selector names with any numbers – appending them like 'element-01' is perfectly OK
  3. avoid characters, which are also used as operators: like the colon for pseudo-elements, plus or tilde for adjacent siblings etc.

#myGradient\:1234{
color:red
}

#myGradient\:1234,
#\31 234myGradient{
color:#000
}

#myGradient-1234{
color:green
}
<ul>
<li id="myGradient-1234">Selectable!</li>
<li id="myGradient:1234">Not selectable: selector contains <strong>colon operator reserved for pseudo elements</strong>. You might however escape the colon by a backslash</li>
<li id="1234myGradient">Not Selectable: selector starts with <strong>numbers</strong></li>
</ul>

Edit: Here's a helper function to check validity.

checkIDs(document.body);

// check id validity
function checkIDs(el) {
  let Ids = el.querySelectorAll('[id]');
  let allIds = [];
  let IdIssues = {
    'non-unique': {},
    'not-selectable': [],
  }
  for (let i = 0; i < Ids.length; i++) {
    let thisId = Ids[i].id;
    if (allIds.indexOf(thisId) == -1) {
      allIds.push(thisId);
    } else {
      let idKey = '\'' + thisId + '\'';
      if (!IdIssues['non-unique'][idKey]) {
        IdIssues['non-unique'][idKey] = 2;
      } else {
        IdIssues['non-unique'][idKey] += 1;
      }
    }
    try {
      let selection = document.querySelector('#' + thisId);
    } catch {
      if (IdIssues['not-selectable'].indexOf(thisId) == -1) {
        IdIssues['not-selectable'].push(thisId);
      }
    }
  }

  let errorCount = 0;
  let nonUniqueCount = Object.keys(IdIssues['non-unique']).length;
  let notSelectableCount = IdIssues['not-selectable'].length;
  errorCount = nonUniqueCount + notSelectableCount;

  if (errorCount) {
    console.log('Id naming issues found:\n' + 'non unique Ids: ' + nonUniqueCount + '\n' + 'not selectable Ids: ' + notSelectableCount);
    console.log(IdIssues);
  } else {
    console.log('Well done! – all Ids are valid and unique!')
  }
}
<svg id="test" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <circle id="circle" fill="none" cx="50%" cy="100%" r="50%" stroke="red" stroke-width="2" />
  <circle id="circle:123" fill="none" cx="25%" cy="25%" r="25%" stroke="green" stroke-width="2" />
</svg>

  <p id="p1@test">test1</p>
  <p id="p1!test">test1</p>
  <p id="c'mon">test1</p>
  <p id="p2">test1</p>
  <p id="p2">test1</p>
  <p id="p2:1:2">test1</p>
  <p id="0123id">test1</p>

This js helper function uses a simple try/catch to check wether an element is actually selectable in DOM or not. So we don't need to check hundreds of renown uncompliant character/naming issues.

Besides, the script will also check non-unique ids that might also cause troubles.

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34