SVG+JavaScript, how to find out if <use> reference exists

Viewed 49

I have an SVG file like this

<svg class="icon" viewBox="0 0 16 16">
  <use href="assets/icons.svg#my-fancy-icon"></use>
</svg>

Using JavaScript, how do I find out if the href attribute of use element points to an element that actually exists?

2 Answers

Get the <use> element's boundaries: width & height: 0 = not existent

Whenever a svg element is referenced by an <use> element via a href attribute
(or the still widely used legacy syntax: xlink:href)
and it can be successfully appended to the svg's shadow DOM, it will return a width and height value > 0.

If not – getBBox() will return a width and height value of 0.
The use reference is not valid/existent.

Example 1: check width and height

let useEls = document.querySelectorAll('use');

useEls.forEach(function(use) {
  let bb = use.getBBox();
  let [width, height] = [bb.width, bb.height];
  if (width == 0 && height == 0) {
    use.closest('svg').classList.add('notavailable')
  }
})
svg {
  height: 10em;
  border: 1px solid #ccc;
  display: inline-block;
}

.notavailable {
  border: 1px solid red;
}
<svg id="svgIcons" class="svgIcons" viewBox="0 0 100 100" style="position:absolute; height:0; width:0;" xmlns="http://www.w3.org/2000/svg">
  <symbol id="home" viewBox="0 0 34 48">
    <path d="M33.16,28.12h-5.2v13h-3.44v-16.72l-7.72-8.72l-7.72,8.72v16.72h-3.44v-13h-5.24l16.4-17.4Z" />
  </symbol>
</svg>

<svg viewBox="0 0 34 48">
  <use href="#home" />
</svg>
<svg viewBox="0 0 34 48">
  <use href="#notExistent" />
</svg>

Example 2: clone all use elements in temporary svg

This way we can also check invisible <use> elements hidden by display: none that would be overlooked by the previous checking method.

checkUseEls();

function checkUseEls() {
  // collect missing references
  let missingRefs = [];
  //add temporary hidden svg
  let svgTmp = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  svgTmp.setAttribute('style', 'position:absolute; width:0; height:0;visibility:hidden');
  document.body.appendChild(svgTmp);

  //add cloned use els
  let useEls = document.querySelectorAll('use');
  useEls.forEach(function(use) {
    let cloned = use.cloneNode();
    cloned.setAttribute('style', 'display:block!important')
    svgTmp.appendChild(cloned)
    let bb = cloned.getBBox();
    let [width, height] = [bb.width, bb.height];
    if (width == 0 && height == 0) {
      missingRefs.push(cloned.getAttribute('href'))
    }
  })
  svgTmp.remove();
  console.log(missingRefs)

}
svg {
  height: 10em;
  border: 1px solid #ccc;
  display: inline-block;
}

.notavailable {
  border: 1px solid red;
}
<svg id="svgIcons" class="svgIcons" viewBox="0 0 100 100" style="display:none" xmlns="http://www.w3.org/2000/svg">
  <symbol id="home" viewBox="0 0 34 48">
    <path d="M33.16,28.12h-5.2v13h-3.44v-16.72l-7.72-8.72l-7.72,8.72v16.72h-3.44v-13h-5.24l16.4-17.4Z" />
  </symbol>
  <symbol id="homeHidden" viewBox="0 0 34 48">
    <path d="M33.16,28.12h-5.2v13h-3.44v-16.72l-7.72-8.72l-7.72,8.72v16.72h-3.44v-13h-5.24l16.4-17.4Z" />
  </symbol>
</svg>

<svg viewBox="0 0 34 48">
  <use href="#home" />
</svg>
<svg viewBox="0 0 34 48" style="display:none">
  <use href="#notExistent" />
</svg>
<svg viewBox="0 0 34 48">
  <use href="#homeHidden" style="display:none"/>
</svg>

Symbol #homeHidden is existent but hidden. By applying display:block to it's cloned instance, we can check it's with/height.

Try this way:

const icon = document.querySelector(".use");

if (icon.firstElementChild === "USE") //...do something
Related