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?
<use>
element's boundaries: width & height: 0 = not existentWhen an SVG element is successfully appended, it returns a width and height value > 0 (calling (getBBox()
)).
If not – getBBox()
will return a width and height value of 0.
The use reference is not valid/existent.
This also applies to elements that are incorrectly appended, e.g. when using document.createElement()
(missing the correct namespace) instead of document.createElementNS()
.
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>
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;
}
<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 the cloned instance, we can check its width/height.
You may also search for all definitions e.g <symbol>
elements
findMissingUseDefs();
function findMissingUseDefs() {
// collect missing references
let missingRefs = [];
//add cloned use els
let useEls = document.querySelectorAll("use");
useEls.forEach((use) => {
let href = use.getAttribute("xlink:href") ?
use.getAttribute("xlink:href") :
use.getAttribute("href");
let id = href.replace("#", "");
let defs = document.querySelectorAll(`${href}`);
if (!defs.length) {
missingRefs.push(href);
}
});
console.log(missingRefs);
}
svg {
height: 10em;
border: 1px solid #ccc;
display: inline-block;
}
<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>
If you're using external SVGs, you must fetch all these files and query the parsed file. Overall, checking BBoxes (getBBox()
) is probably the easiest way.