Reputation: 56754
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).
Upvotes: 4
Views: 3829
Reputation: 8801
Similar to the answer above, without HTMLElement.prototype
and the addition of style.visibility
check
function isHidden(elem) {
if (document === elem) return false;
if ('none' == elem.style.display || 'hidden' == elem.style.visibility) return true;
var cs = getComputedStyle(elem);
if ('none' === cs.display || 'hidden' === cs.visibility) return true;
if (elem.parentNode) return isHidden(elem.parentNode);
return false;
}
Upvotes: 0
Reputation: 740
For a pure function that:
You can define the below function and then call it on the element you wish to validate:
function isVisible(element) {
// Start with the element itself and move up the DOM tree
for (let el = element; el && el !== document; el = el.parentNode) {
// If current element has display property 'none', return false
if (getComputedStyle(el).display === "none") {
return false;
}
}
// Neither element itself nor any parents have display 'none', so return true
return true;
}
Upvotes: 0
Reputation:
Not sure about performance, but it should be faster than your approach at least:
HTMLElement.prototype.isInvisible = function() {
if (this.style.display == 'none') return true;
if (getComputedStyle(this).display === 'none') return true;
if (this.parentNode.isInvisible) return this.parentNode.isInvisible();
return false;
};
Upvotes: 4
Reputation: 10945
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
Upvotes: 10