0

Given a snippet of SVG like this:

<path d="..." fill-rule="invalid-value" />

What is the best in-JS way to detect that the value for the fill-rule attribute is not valid?In the MDN spec it must be either nonzero or evenodd.

I want to validate all standard attributes and am trying to avoid writing a custom parser for each standard attribute.

1 Answers1

0

To some extent you might find invalid values by comparing attribute and computed style values.

Example 1: check single attribute

let els = document.querySelectorAll("path, polygon");
els.forEach(function(path) {
  let fiilRuleAtt = path.getAttribute("fill-rule");
  let pathStyle = window.getComputedStyle(path);
  let fiilRuleStyle = pathStyle.fillRule;

  if (fiilRuleAtt != fiilRuleStyle) {
    console.log(`${path.id}  has invalid attribute value: "${fiilRuleAtt}". Reset to css value: ${fiilRuleStyle}`);
  }
});
svg {
  width: 50%;
  display: inline-block;
}
<svg viewBox="-10 -10 320 120" xmlns="http://www.w3.org/2000/svg">
  <polygon id="star" fill-rule="nonzero" stroke="red" points="50,0 21,90 98,35 2,35 79,90" />
  <path id="rect1" fill-rule="nonsenseFillRule" stroke="red" d="M110,0  h90 v90 h-90 z
           M130,20 h50 v50 h-50 z" />
  <path id="rect2" fill-rule="nonzero" stroke="red" d="M210,0  h90 v90 h-90 z
           M230,20 v50 h50 v-50 z" />
</svg>

Example 2: check all attributes, except excluded

We can define an array of non style related attributes like: id, class etc.
Besides, data-attributes are also ignored.

let els = document.querySelectorAll("path, polygon");
let excludedAtts = ["id", "class", "points", "d", "fill", "stroke"];

els.forEach(function (el) {
  let attributes = [...el.attributes];
  attributes.forEach(function (att, i) {
    let attName = att.nodeName;
    let attNameJs = camalize(attName);
    //exclude properties defined in array and also data attributes
    if (excludedAtts.indexOf(attName) == -1 && attName.indexOf("data-") == -1) {
      let attValue = el.getAttribute(attName);
      let elStyle = window.getComputedStyle(el);
      let styleValue = elStyle[attNameJs];
      //not supported properties
      if (styleValue === undefined) {
        console.log(
          `${el.id}  has unsupported property "${attName}": "${attValue}".`
        );
      } else {
        //strip units
        attValue = attValue.split(/[, ]/).filter(Boolean).map( (val)=>{return val.replaceAll('px','')} ).join(' ');
        let styleValueUnitless = styleValue.split(/[, ]/).filter(Boolean).map( (val)=>{return val.replaceAll('px','')} ).join(' ');
        styleValue = attValue === styleValueUnitless ? styleValueUnitless : styleValue;
        if (attValue != styleValue) {
          console.log(
            `${el.id}  has invalid ${attName} value: "${attValue}". Reset to css value: ${styleValue}`
          );
        }
      }
    }
  });
});


/**
 * @smilyface: https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case#52551910
 **/
function camalize(str) {
  return str
    .toLowerCase()
    .replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase());
}
svg {
  width: 50%;
  display: inline-block;
}
<svg viewBox="-10 -10 320 120" xmlns="http://www.w3.org/2000/svg">
  <polygon id="star" fill-rule="nonzero" stroke="red" points="50,0 21,90 98,35 2,35 79,90" />
  <path id="rect1" fill-rule="notValid" stroke="red" d="M110,0  h90 v90 h-90 z M130,20 h50 v50 h-50 z" />
  <path id="rect2" data-id="test" rotate="33" fill-rule="nonzero" stroke="red" stroke-dasharray="auto" d="M210,0  h90 v90 h-90 z M230,20 v50 h50 v-50 z" />
</svg>

But most likely you will also receive false positives for newer/experimental features that are not yet supported by your current browser.

herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
  • This is very promising and close to what I'm looking for. Thank you! – Gavin Atkinson Aug 19 '22 at 16:00
  • @Gavin Atkinson: I've updated the snippet above. Now you can also find (at least some) unsupported properties that would return undefined. stroke-dasharray should also work. transform should be included, due to the differences between svg and css syntax - like rotate (45 10 10). – herrstrietzel Aug 20 '22 at 14:26