Проверьте, отображается ли элемент в DOM

Можно ли как-то проверить, виден ли элемент в чистом JS (нет jQuery)?

Так, например, на этой странице: Performance Bikes, если вы наводите курсор на Deals (в верхнем меню), появляется окно сделок, но в начале оно не отображалось. Это в HTML, но это не видно.

Итак, учитывая элемент DOM, как я могу проверить, является ли он видимым или нет? Я старался:

window.getComputedStyle(my_element)['display']);

но это не похоже на работу. Интересно, какие атрибуты я должен проверить. Это приходит мне в голову:

display !== 'none'
visibility !== 'hidden'

Какие-нибудь другие, которые я мог бы пропустить?

Ответ 1

Согласно этой документации MDN, элемент offsetParent свойство вернет null, когда он, или любой из его родителей, скрыт с помощью свойства отображения стиля. Просто убедитесь, что элемент не зафиксирован. Скрипт для проверки этого, если у вас нет position: fixed; элементы на вашей странице могут выглядеть так:

// Where el is the DOM element you'd like to test for visibility
function isHidden(el) {
    return (el.offsetParent === null)
}

С другой стороны, если у вас есть элементы с фиксированным положением, которые могут попасть в этот поиск, вам, к сожалению (и медленно), придется использовать window.getComputedStyle(). Функция в этом случае может быть:

// Where el is the DOM element you'd like to test for visibility
function isHidden(el) {
    var style = window.getComputedStyle(el);
    return (style.display === 'none')
}

Вариант № 2, вероятно, немного проще, так как он учитывает больше крайних случаев, но я держу пари, что он тоже намного медленнее, поэтому, если вам придется повторять эту операцию много раз, лучше всего избегать ее.

Ответ 2

Все другие решения сломались для какой-то ситуации для меня.

Посмотрите выигрышный ответ на:

http://plnkr.co/edit/6CSCA2fe4Gqt4jCBP2wu?p=preview

В конце концов, я решил, что лучшим решением было $(elem).is(':visible') - однако это не чистый javascript. это jquery..

поэтому я заглянул в их источник и нашел, что я хотел

jQuery.expr.filters.visible = function( elem ) {
    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

Это источник: https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js

Ответ 3

Если вы заинтересованы в видимости пользователем:

function isVisible(elem) {
    if (!(elem instanceof Element)) throw Error('DomUtil: elem is not an element.');
    const style = getComputedStyle(elem);
    if (style.display === 'none') return false;
    if (style.visibility !== 'visible') return false;
    if (style.opacity < 0.1) return false;
    if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
        elem.getBoundingClientRect().width === 0) {
        return false;
    }
    const elemCenter   = {
        x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
        y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
    };
    if (elemCenter.x < 0) return false;
    if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
    if (elemCenter.y < 0) return false;
    if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
    let pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y);
    do {
        if (pointContainer === elem) return true;
    } while (pointContainer = pointContainer.parentNode);
    return false;
}

Протестировано (с использованием mocha терминология):

describe.only('visibility', function () {
    let div, visible, notVisible, inViewport, leftOfViewport, rightOfViewport, aboveViewport,
        belowViewport, notDisplayed, zeroOpacity, zIndex1, zIndex2;
    before(() => {
        div = document.createElement('div');
        document.querySelector('body').appendChild(div);
        div.appendChild(visible = document.createElement('div'));
        visible.style       = 'border: 1px solid black; margin: 5px; display: inline-block;';
        visible.textContent = 'visible';
        div.appendChild(inViewport = visible.cloneNode(false));
        inViewport.textContent = 'inViewport';
        div.appendChild(notDisplayed = visible.cloneNode(false));
        notDisplayed.style.display = 'none';
        notDisplayed.textContent   = 'notDisplayed';
        div.appendChild(notVisible = visible.cloneNode(false));
        notVisible.style.visibility = 'hidden';
        notVisible.textContent      = 'notVisible';
        div.appendChild(leftOfViewport = visible.cloneNode(false));
        leftOfViewport.style.position = 'absolute';
        leftOfViewport.style.right = '100000px';
        leftOfViewport.textContent = 'leftOfViewport';
        div.appendChild(rightOfViewport = leftOfViewport.cloneNode(false));
        rightOfViewport.style.right       = '0';
        rightOfViewport.style.left       = '100000px';
        rightOfViewport.textContent = 'rightOfViewport';
        div.appendChild(aboveViewport = leftOfViewport.cloneNode(false));
        aboveViewport.style.right       = '0';
        aboveViewport.style.bottom       = '100000px';
        aboveViewport.textContent = 'aboveViewport';
        div.appendChild(belowViewport = leftOfViewport.cloneNode(false));
        belowViewport.style.right       = '0';
        belowViewport.style.top       = '100000px';
        belowViewport.textContent = 'belowViewport';
        div.appendChild(zeroOpacity = visible.cloneNode(false));
        zeroOpacity.textContent   = 'zeroOpacity';
        zeroOpacity.style.opacity = '0';
        div.appendChild(zIndex1 = visible.cloneNode(false));
        zIndex1.textContent = 'zIndex1';
        zIndex1.style.position = 'absolute';
        zIndex1.style.left = zIndex1.style.top = zIndex1.style.width = zIndex1.style.height = '100px';
        zIndex1.style.zIndex = '1';
        div.appendChild(zIndex2 = zIndex1.cloneNode(false));
        zIndex2.textContent = 'zIndex2';
        zIndex2.style.left = zIndex2.style.top = '90px';
        zIndex2.style.width = zIndex2.style.height = '120px';
        zIndex2.style.backgroundColor = 'red';
        zIndex2.style.zIndex = '2';
    });
    after(() => {
        div.parentNode.removeChild(div);
    });
    it('isVisible = true', () => {
        expect(isVisible(div)).to.be.true;
        expect(isVisible(visible)).to.be.true;
        expect(isVisible(inViewport)).to.be.true;
        expect(isVisible(zIndex2)).to.be.true;
    });
    it('isVisible = false', () => {
        expect(isVisible(notDisplayed)).to.be.false;
        expect(isVisible(notVisible)).to.be.false;
        expect(isVisible(document.createElement('div'))).to.be.false;
        expect(isVisible(zIndex1)).to.be.false;
        expect(isVisible(zeroOpacity)).to.be.false;
        expect(isVisible(leftOfViewport)).to.be.false;
        expect(isVisible(rightOfViewport)).to.be.false;
        expect(isVisible(aboveViewport)).to.be.false;
        expect(isVisible(belowViewport)).to.be.false;
    });
});

Ответ 4

Это может помочь: Скройте элемент, поместив его в крайнее левое положение, а затем проверьте свойство offsetLeft. Если вы хотите использовать jQuery, вы можете просто проверить селектор : visible и получить видимое состояние элемента.

HTML:

<div id="myDiv">Hello</div>

CSS:

<!-- for javaScript-->
#myDiv{
   position:absolute;
   left : -2000px;
}

<!-- for jQuery -->
#myDiv{
    visibility:hidden;
}

javaScript:

var myStyle = document.getElementById("myDiv").offsetLeft;

if(myStyle < 0){
     alert("Div is hidden!!");
}

jQuery:

if(  $("#MyElement").is(":visible") == true )
{  
     alert("Div is hidden!!");        
}

jsFiddle

Ответ 5

Использовать тот же код, что и jQuery:

jQuery.expr.pseudos.visible = function( elem ) {
    return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};

Итак, в функции:

function isVisible(e) {
    return !!( e.offsetWidth || e.offsetHeight || e.getClientRects().length );
}

Работает как очарование в моей Win/IE10, Linux/Firefox.45, Linux/Chrome.52...

Большое спасибо jQuery без jQuery!

Ответ 6

Объединяя пару ответов выше:

function isVisible (ele) {
    var style = window.getComputedStyle(ele);
    return  style.width !== "0" &&
    style.height !== "0" &&
    style.opacity !== "0" &&
    style.display!=='none' &&
    style.visibility!== 'hidden';
}

Как сказал AlexZ, это может быть медленнее, чем некоторые другие ваши варианты, если вы более точно знаете, что ищете, но это должно охватить все основные способы скрытия элементов.

Но это также зависит от того, что считается видимым для вас. Например, высоту div можно установить равной 0px, но содержимое все равно отображается в зависимости от свойств переполнения. Либо содержимое div может быть такого же цвета, что и фон, поэтому оно невидимо для пользователей, но все равно отображается на странице. Либо элемент div может быть перемещен за пределы экрана или скрыт за другими элементами div, либо его содержимое может быть невидимым, но граница все еще видна. В определенной степени "видимый" является субъективным термином.

Ответ 7

Если элемент виден регулярно (display: block и visibillity: visible), но некоторый родительский контейнер скрыт, то мы можем использовать clientWidth и clientHeight для проверки этого.

function isVisible (ele) {
  return  ele.clientWidth !== 0 &&
    ele.clientHeight !== 0 &&
    ele.style.opacity !== 0 &&
    ele.style.visibility !== 'hidden';
}

Плункер (нажмите здесь)

Ответ 8

У меня есть более эффективное решение по сравнению с решением AlexZ getComputedStyle(), когда у вас есть фиксированные позиции позиции, если вы хотите игнорировать некоторые случаи краев (проверьте комментарии):

function isVisible(el) {
    /* offsetParent would be null if display 'none' is set.
       However Chrome, IE and MS Edge returns offsetParent as null for elements
       with CSS position 'fixed'. So check whether the dimensions are zero.

       This check would be inaccurate if position is 'fixed' AND dimensions were
       intentionally set to zero. But..it is good enough for most cases.*/
    if (!el.offsetParent && el.offsetWidth === 0 && el.offsetHeight === 0) {
        return false;
    }
    return true;
}

Боковое примечание. Строго говоря, сначала необходимо определить "видимость". В моем случае я рассматриваю элемент видимым, пока я не могу без проблем запускать все методы/свойства DOM (даже если непрозрачность 0 или свойство видимости CSS "скрыто" и т.д.).

Ответ 9

Если мы просто собираем основные способы обнаружения видимости, позвольте мне не забыть:

opacity > 0.01; // probably more like .1 to actually be visible, but YMMV

А как получить атрибуты:

element.getAttribute(attributename);

Итак, в вашем примере:

document.getElementById('snDealsPanel').getAttribute('visibility');

Но что? Здесь это не работает. Посмотрите ближе, и вы обнаружите, что видимость обновляется не как атрибут элемента, а с использованием свойства style. Это одна из многих проблем с попыткой сделать то, что вы делаете. Среди прочего: вы не можете гарантировать, что на самом деле есть что-то, что можно увидеть в элементе, только потому, что его видимость, отображение и непрозрачность имеют правильные значения. У него все еще может отсутствовать контент, или ему может не хватить высоты и ширины. Другой объект может скрыть его. Для более подробной информации быстрый поиск в Google показывает этот и даже включает библиотеку, чтобы попытаться решить проблему. (YMMV)

Проверьте следующие, которые являются возможными дубликатами этого вопроса, с отличными ответами, включая некоторое понимание могущественного Джона Ресига. Однако ваш конкретный вариант использования немного отличается от стандартного, поэтому я воздержусь от отметки:

(EDIT: OP ГОВОРИТ ОГРАНИЧЕНИЯ СТРАНИЦ, НЕ СОЗДАВАЯ ИХ, ТАК НИЖЕ НЕ ПРИМЕНЯЕТСЯ) Лучший вариант? Привяжите видимость элементов к свойствам модели и всегда создавайте видимость, зависящую от этой модели, так же, как Angular делает с ng-show. Вы можете сделать это с помощью любого инструмента, который вы хотите: Angular, простой JS, что угодно. Еще лучше, вы можете изменить реализацию DOM с течением времени, но вы всегда сможете прочитать состояние модели, а не DOM. Чтение вашей правды из DOM - это плохо. И медленно. Намного лучше проверить модель и довериться вашей реализации, чтобы убедиться, что состояние DOM отражает модель. (И используйте автоматическое тестирование, чтобы подтвердить это предположение.)

Ответ 10

Итак, я нашел наиболее приемлемый метод:

function visible(elm) {
  if(!elm.offsetHeight && !elm.offsetWidth) { return false; }
  if(getComputedStyle(elm).visibility === 'hidden') { return false; }
  return true;
}

Это основывается на следующих фактах:

  • Элемент display: none (даже вложенный) не имеет ширины и высоты.
  • visiblity равно hidden даже для вложенных элементов.

Так что не нужно тестировать offsetParent или зацикливать в дереве DOM, чтобы проверить, у какого родителя есть visibility: hidden. Это должно работать даже в IE 9.

Вы можете утверждать, что opacity: 0 и свернутые элементы (с шириной, но без высоты - или наоборот) тоже не видны. Но опять же они не говорят, что скрыты.

Ответ 11

Небольшое дополнение к ответу ohad navon.

Если центр элемента принадлежит другому элементу, мы его не найдем.

Итак, чтобы убедиться, что одна из точек элемента окажется видимой

function isElementVisible(elem) {
    if (!(elem instanceof Element)) throw Error('DomUtil: elem is not an element.');
    const style = getComputedStyle(elem);
    if (style.display === 'none') return false;
    if (style.visibility !== 'visible') return false;
    if (style.opacity === 0) return false;
    if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
        elem.getBoundingClientRect().width === 0) {
        return false;
    }
    var elementPoints = {
        'center': {
            x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
            y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
        },
        'top-left': {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().top
        },
        'top-right': {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().top
        },
        'bottom-left': {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().bottom
        },
        'bottom-right': {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().bottom
        }
    }

    for(index in elementPoints) {
        var point = elementPoints[index];
        if (point.x < 0) return false;
        if (point.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
        if (point.y < 0) return false;
        if (point.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
        let pointContainer = document.elementFromPoint(point.x, point.y);
        if (pointContainer !== null) {
            do {
                if (pointContainer === elem) return true;
            } while (pointContainer = pointContainer.parentNode);
        }
    }
    return false;
}

Ответ 12

Код jQuery из http://code.jquery.com/jquery-1.11.1.js имеет isHidden param

var isHidden = function( elem, el ) {
    // isHidden might be called from jQuery#filter function;
    // in that case, element will be second argument
    elem = el || elem;
    return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
};

Итак, похоже, что есть дополнительная проверка, связанная с документом владельца

Интересно, действительно ли это происходит в следующих случаях:

  • Элементы, скрытые за другими элементами на основе zIndex
  • Элементы с прозрачностью, делающие их невидимыми
  • Элементы, расположенные за пределами экрана (т.е. слева: -1000px)
  • Элементы с видимостью: скрытые
  • Элементы с дисплеем: none
  • Элементы без видимого текста или подэлементов
  • Элементы с высотой или шириной, установленными на 0

Ответ 13

Просто для ссылки следует отметить, что getBoundingClientRect() может работать в определенных случаях.

Например, простая проверка того, что элемент скрыт с помощью display: none, может выглядеть примерно так:

var box = element.getBoundingClientRect();
var visible = box.width && box.height;

Это также удобно, потому что оно также охватывает случаи с нулевой шириной, нулевой высотой и position: fixed. Однако он не должен сообщать о элементах, скрытых с помощью opacity: 0 или visibility: hidden (но ни один из них не будет offsetParent).

Ответ 14

Улучшение @Guy Messika ответ выше, ломая и возвращая ложь, если центральная точка 'X является <0, является неправильным, поскольку правая сторона элемента может перейти в представление. здесь исправление:

private isVisible(elem) {
    const style = getComputedStyle(elem);

    if (style.display === 'none') return false;
    if (style.visibility !== 'visible') return false;
    if ((style.opacity as any) === 0) return false;

    if (
        elem.offsetWidth +
        elem.offsetHeight +
        elem.getBoundingClientRect().height +
        elem.getBoundingClientRect().width === 0
    ) return false;

    const elementPoints = {
        center: {
            x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
            y: elem.getBoundingClientRect().top + elem.offsetHeight / 2,
        },
        topLeft: {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().top,
        },
        topRight: {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().top,
        },
        bottomLeft: {
            x: elem.getBoundingClientRect().left,
            y: elem.getBoundingClientRect().bottom,
        },
        bottomRight: {
            x: elem.getBoundingClientRect().right,
            y: elem.getBoundingClientRect().bottom,
        },
    };

    const docWidth = document.documentElement.clientWidth || window.innerWidth;
    const docHeight = document.documentElement.clientHeight || window.innerHeight;

    if (elementPoints.topLeft.x > docWidth) return false;
    if (elementPoints.topLeft.y > docHeight) return false;
    if (elementPoints.bottomRight.x < 0) return false;
    if (elementPoints.bottomRight.y < 0) return false;

    for (let index in elementPoints) {
        const point = elementPoints[index];
        let pointContainer = document.elementFromPoint(point.x, point.y);
        if (pointContainer !== null) {
            do {
                if (pointContainer === elem) return true;
            } while (pointContainer = pointContainer.parentNode);
        }
    }
    return false;
}

Ответ 15

Это возвращает true тогда и только тогда, когда элемент и все его предки видны. Он смотрит только на свойства стиля display и visibility:

    var isVisible = function(el){
        // returns true iff el and all its ancestors are visible
        return el.style.display !== 'none' && el.style.visibility !== 'hidden'
        && (el.parentElement? isVisible(el.parentElement): true)
    };

Ответ 16

Это способ определить его для всех свойств css, включая видимость:

HTML:

<div id="element">div content</div>

CSS

#element
{
visibility:hidden;
}

JavaScript:

var element = document.getElementById('element');
 if(element.style.visibility == 'hidden'){
alert('hidden');
}
else
{
alert('visible');
}

Он работает для любого свойства css и является очень универсальным и надежным.

Ответ 17

Здесь код, который я написал, чтобы найти единственное видимое среди нескольких подобных элементов и вернуть значение его атрибута "class" без jQuery:

  // Build a NodeList:
  var nl = document.querySelectorAll('.myCssSelector');

  // convert it to array:
  var myArray = [];for(var i = nl.length; i--; myArray.unshift(nl[i]));

  // now find the visible (= with offsetWidth more than 0) item:
  for (i =0; i < myArray.length; i++){
    var curEl = myArray[i];
    if (curEl.offsetWidth !== 0){
      return curEl.getAttribute("class");
    }
  }

Ответ 18

Вот что я сделал:

HTML & CSS: элемент по умолчанию скрыт

<html>
<body>

<button onclick="myFunction()">Click Me</button>

<p id="demo" style ="visibility: hidden;">Hello World</p> 

</body>
</html> 

JavaScript: добавлен код для проверки того, видимость скрыта или нет:

<script>
function myFunction() {
   if ( document.getElementById("demo").style.visibility === "hidden"){
   document.getElementById("demo").style.visibility = "visible";
   }
   else document.getElementById("demo").style.visibility = "hidden";
}
</script>