Отличный способ узнать, есть ли у элемента или любого из его родительских элементов отображение: нет
Мне нужно найти очень эффективный способ выяснить, имеет ли пользовательский элемент или какой-либо из его родительских элементов display: none;
Первый подход:
checkVisible() {
let parentNodes = [];
let el = this;
while (!!(el = el.parentNode)) {
parentNodes.push(el);
}
return [this, ...parentNodes].some(el => el.style.display === 'none')
}
Есть ли что-нибудь, что работает быстрее, чем это? Это даже безопасный метод?
Причина, по которой мне это нужно: у нас есть <data-table>
пользовательский элемент (нативный веб-компонент), который делает очень тяжелым connectedCallback()
, У нас есть приложение, содержащее около 20-30 таких пользовательских элементов на одной странице, что приводит к тому, что IE 11 занимает около 15 секунд до визуализации страницы.
Мне нужно отложить инициализацию тех, <data-table>
компоненты, которые изначально даже не видны, поэтому мне нужен способ проверить внутри connectedCallback()
если элемент виден (чего нет, если он находится на одной из 18 вкладок, изначально не показанных).
3 ответа
Самый простой способ увидеть, имеет ли элемент или его родитель display:none
это использовать 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>
Этот код конвертирует el.offsetParent
в логическое значение, которое указывает, показывает ли элемент или нет.
Это работает только для
display:none
Не уверен насчет производительности, но он должен быть быстрее, чем ваш подход, по крайней мере:
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;
};
Для чистой функции, которая:
- возвращает TRUE тогда и только тогда, когда ни сам элемент, ни любой из его родителей вплоть до самого документа не имеют отображения стиля === 'none'
- возвращает FALSE тогда и только тогда, когда либо сам элемент, либо любой родительский элемент вплоть до самого документа имеют отображение стиля === 'none'
Вы можете определить приведенную ниже функцию, а затем вызвать ее для элемента, который хотите проверить:
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;
}