I need to find a very performant way to find out if a custom element or any of its parent elements has display: none;
First approach:
checkVisible() {
let parentNodes = [];
let el = this;
while (!!(el = el.parentNode)) {
parentNodes.push(el);
}
return [this, ...parentNodes].some(el => getComputedStyle(el).display === 'none')
}
Is there anything that runs faster than this? Is this even a safe method?
The reason I need this: We have a <data-table>
custom element (native webcomponent) which does very heavy lifting in its connectedCallback()
. We have an application that has like 20-30 of those custom elements in a single page, which leads to IE 11 taking like 15 seconds until the page is rendered.
I need to delay initialisation of those <data-table>
components which are initially not even visible, so I need a way to test inside the connectedCallback()
if the element is visible (which it is not if it is in one of the 18 tabs initially not shown).
The easiest way to see if an element or its parent has display:none
is to use el.offsetParent
.
const p1 = document.getElementById('parent1');
const p2 = document.getElementById('parent2');
const c1 = document.getElementById('child1');
const c2 = document.getElementById('child2');
const btn = document.getElementById('btn');
const output = document.getElementById('output');
function renderVisibility() {
const p1state = isElementVisible(p1) ? 'is visible' : 'is not visible';
const p2state = isElementVisible(p2) ? 'is visible' : 'is not visible';
const c1state = isElementVisible(c1) ? 'is visible' : 'is not visible';
const c2state = isElementVisible(c2) ? 'is visible' : 'is not visible';
output.innerHTML = `Parent 1 ${p1state}<br>Parent 2 ${p2state}<br/>Child 1 ${c1state}<br/>Child 2 ${c2state}`;
}
function isElementVisible(el) {
return !!el.offsetParent;
}
function toggle() {
p1.style.display = (p1.style.display ? '' : 'none');
p2.style.display = (p2.style.display ? '' : 'none');
renderVisibility();
}
btn.addEventListener('click', toggle),
renderVisibility();
<div id="parent1" style="display:none">
<div id="child1">child 1</div>
</div>
<div id="parent2">
<div id="child2">second child</div>
</div>
<button id="btn">Toggle</button>
<hr>
<div id="output"></div>
This code converts el.offsetParent
into a boolean that indicates if the element is showing or not.
This only works for
display:none