Como posso verificar se um elemento é realmente visível com JavaScript? [duplicado]

StackOverflow https://stackoverflow.com/questions/704758

  •  22-08-2019
  •  | 
  •  

Pergunta

Esta questão já tem uma resposta aqui:

Em JavaScript, como você verificar se um elemento é realmente visível?

Eu não me refiro apenas a verificação dos atributos visibility e display. Quero dizer, verificando que o elemento não é

  • visibility: hidden ou display: none
  • debaixo de um outro elemento
  • rolada para fora da borda da tela

Por razões técnicas não posso incluir todos os scripts. No entanto, posso usar Prototype como é na página já.

Foi útil?

Solução

Para o ponto 2.

Eu vejo que ninguém tenha sugerido o uso de document.elementFromPoint(x,y), para mim é a maneira mais rápida para testar se um elemento está aninhado ou oculta pelo outro. Você pode passar os deslocamentos do elemento targetted para a função.

página de teste PPK está aqui na elementFromPoint .

Outras dicas

Eu não sei o quanto isso é suportado em navegadores mais antigos ou não tão modernos, mas eu estou usando algo parecido com isso (sem o neeed para quaisquer bibliotecas):

function visible(element) {
  if (element.offsetWidth === 0 || element.offsetHeight === 0) return false;
  var height = document.documentElement.clientHeight,
      rects = element.getClientRects(),
      on_top = function(r) {
        var x = (r.left + r.right)/2, y = (r.top + r.bottom)/2;
        return document.elementFromPoint(x, y) === element;
      };
  for (var i = 0, l = rects.length; i < l; i++) {
    var r = rects[i],
        in_viewport = r.top > 0 ? r.top <= height : (r.bottom > 0 && r.bottom <= height);
    if (in_viewport && on_top(r)) return true;
  }
  return false;
}

Ele verifica que o elemento tem uma área> 0 e, em seguida, ele verifica se qualquer parte do elemento está dentro da janela e que não está escondido "em" um outro elemento (na verdade eu só verificar em um único ponto no centro do elemento, por isso não é 100% certeza - mas você poderia simplesmente modificar o script para itterate sobre todos os pontos do elemento, se você realmente precisa para ...)

.

Atualização

Modificado on_top função que verificar cada pixel:

on_top = function(r) {
  for (var x = Math.floor(r.left), x_max = Math.ceil(r.right); x <= x_max; x++)
  for (var y = Math.floor(r.top), y_max = Math.ceil(r.bottom); y <= y_max; y++) {
    if (document.elementFromPoint(x, y) === element) return true;
  }
  return false;
};

Não sei sobre o desempenho:)

Como jkl apontou, verificando a visibilidade ou a exibição do elemento não é suficiente. Você tem que verificar seus antepassados. O selênio faz isso quando se verifica a visibilidade em um elemento.

Confira o Selenium.prototype.isVisible método no arquivo de selênio-api.js.

http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails/selenium-core/scripts/selenium-api.js

É uma pergunta interessante.

Esta seria a minha abordagem.

  1. No primeiro cheque que element.style.visibility! == 'escondido' && element.style.display! == 'none'
  2. teste Em seguida, com document.elementFromPoint (element.offsetLeft, element.offsetTop) se o elemento retornado é o elemento que eu esperar, isso é complicado para detectar se um elemento está sobrepondo outra completamente.
  3. Finalmente teste se offsetTop e offsetLeft estão localizados em compensações a janela tomada de rolagem em conta.

Hope isso ajuda.

Isto é o que eu tenho até agora. Abrange tanto 1 e 3. Eu estou no entanto, ainda lutando com 2 desde que eu não sou tão familiarizado com Prototype (eu sou mais um tipo jQuery de cara).

function isVisible( elem ) {
    var $elem = $(elem);

    // First check if elem is hidden through css as this is not very costly:
    if ($elem.getStyle('display') == 'none' || $elem.getStyle('visibility') == 'hidden' ) {
        //elem is set through CSS stylesheet or inline to invisible
        return false;
    }

    //Now check for the elem being outside of the viewport
    var $elemOffset = $elem.viewportOffset();
    if ($elemOffset.left < 0 || $elemOffset.top < 0) {
        //elem is left of or above viewport
        return false;
    }
    var vp = document.viewport.getDimensions();
    if ($elemOffset.left > vp.width || $elemOffset.top > vp.height) {
        //elem is below or right of vp
        return false;
    }

    //Now check for elements positioned on top:
    //TODO: Build check for this using Prototype...
    //Neither of these was true, so the elem was visible:
    return true;
}

O Prototype Elemento biblioteca é uma das bibliotecas de consulta mais poderosas em termos dos métodos. Eu recomendo que você confira a API.

Algumas sugestões:

  1. Verificar a visibilidade pode ser uma dor, mas você pode usar o método e Element.getStyle() métodos Element.visible() combinados em uma função personalizada. Com getStyle() você pode conferir o estilo computadorizada real.

  2. Eu não sei exatamente o que você quer dizer com "embaixo" :) Se você entende por que tem um ancestral específico, por exemplo, uma div wrapper, você pode usar Element.up(cssRule):

    var child = $("myparagraph");
    if(!child.up("mywrapper")){
      // I lost my mom!
    }
    else {
      // I found my mom!
    }
    

    Se você quiser verificar os irmãos do elemento filho que você pode fazer isso também:

    var child = $("myparagraph");
    if(!child.previous("mywrapper")){
      // I lost my bro!
    } 
    else {
      // I found my bro!
    }
    
  3. Mais uma vez, Elemento lib pode ajudá-lo se eu entendi corretamente o que quer dizer :) Você pode verificar o real dimensões da janela eo deslocamento de seu elemento para que você possa calcular se o seu elemento é "fora da tela".

Boa sorte!

Eu colei um caso de teste para PrototypeJS em http://gist.github.com/117125 . Parece, no seu caso, simplesmente não pode confiar em getStyle() em tudo. Para maximizar a confiabilidade da função isMyElementReallyVisible você deve combinar o seguinte:

  • Verificar o estilo computadorizada (dojo tem um bom implementação que você pode pedir emprestado)
  • Verificar o viewportoffset (método protótipo nativo)
  • Verificar o índice z para o "abaixo" problema (no Internet Explorer pode ser buggy)

Uma maneira de fazer isso é:

isVisible(elm) {
    while(elm.tagName != 'BODY') {
        if(!$(elm).visible()) return false;
        elm = elm.parentNode;
    }
    return true;
}

Créditos: https: // github. com / atetlaw / Really-Fácil-campo-validação / blob / master / validation.js # L178

Captura de rato-de arrastar e janela de visualização de eventos (onmouseup, onresize, onscroll).

Quando um arrasto extremidades fazer uma comparação entre o arrastou fronteira artigo com todos os "elementos de interesse" (ou seja, elementos com class "dont_hide" ou uma matriz de ids). Faça o mesmo com window.onscroll e window.onresize. Marque quaisquer elementos escondidos com um atributo especial ou classname ou simplesmente executar qualquer ação que você deseja, em seguida, e ali.

Os testes escondidos são bastante fáceis. Para "totalmente escondido" você quer saber se cantos TODOS são ou dentro do limite arrastou-item ou fora da janela. Para parcialmente escondido você está procurando um único canto combinando o mesmo teste.

Eu não acho que a verificação próprias propriedades de visibilidade e de apresentação do elemento é bom o suficiente para a exigência # 1, mesmo se você usar currentStyle / getComputedStyle. Você também tem que verificar ancestrais do elemento. Se um antepassado está escondido, então é o elemento.

Eu odiaria redirecioná-lo para jQuery (como frequentemente é feito), , mas essa discussão sobre quando os elementos são realmente visível é muito perspicaz.

E desde jQuery 1.3 .2 este é deixou de ser um problema .

propriedade offsetHeight Check elementos. Se ele é mais do que 0, é visível. Nota: esta abordagem não cobrir uma situação quando visibility: hidden estilo é definido. Mas que estilo é algo estranho de qualquer maneira.

Aqui está um script de exemplo e caso de teste. Tampas posicionados elementos, visibilidade: oculto, exposição: nenhum. Será que não teste z-index, assumir que funciona.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title></title>
    <style type="text/css">
    div {
      width: 200px;
      border: 1px solid red;
    }
    p {
      border: 2px solid green;
    }
    .r {
      border: 1px solid #BB3333;
      background: #EE9999;
      position: relative;
      top: -50px;
      height: 2em;
    }
    .of {
      overflow: hidden;
      height: 2em;
      word-wrap: none; 
    }
    .of p {
      width: 100%;
    }

    .of pre {
      display: inline;
    }
    .iv {
      visibility: hidden;
    }
    .dn {
      display: none;
    }
    </style>
    <script src="http://www.prototypejs.org/assets/2008/9/29/prototype-1.6.0.3.js"></script>
    <script>
      function isVisible(elem){
        if (Element.getStyle(elem, 'visibility') == 'hidden' || Element.getStyle(elem, 'display') == 'none') {
          return false;
        }
        var topx, topy, botx, boty;
        var offset = Element.positionedOffset(elem);
        topx = offset.left;
        topy = offset.top;
        botx = Element.getWidth(elem) + topx;
        boty = Element.getHeight(elem) + topy;
        var v = false;
        for (var x = topx; x <= botx; x++) {
          for(var y = topy; y <= boty; y++) {
            if (document.elementFromPoint(x,y) == elem) {
              // item is visible
              v = true;
              break;
            }
          }
          if (v == true) {
            break;
          }
        }
        return v;
      }

      window.onload=function() {
        var es = Element.descendants('body');
        for (var i = 0; i < es.length; i++ ) {
          if (!isVisible(es[i])) {
            alert(es[i].tagName);
          }
        }
      }
    </script>
  </head>
  <body id='body'>
    <div class="s"><p>This is text</p><p>More text</p></div>
    <div class="r">This is relative</div>
    <div class="of"><p>This is too wide...</p><pre>hidden</pre>
    <div class="iv">This is invisible</div>
    <div class="dn">This is display none</div>
  </body>
</html>
/**
 * Checks display and visibility of elements and it's parents
 * @param  DomElement  el
 * @param  boolean isDeep Watch parents? Default is true
 * @return {Boolean}
 *
 * @author Oleksandr Knyga <oleksandrknyga@gmail.com>
 */
function isVisible(el, isDeep) {
    var elIsVisible = true;

    if("undefined" === typeof isDeep) {
        isDeep = true;
    }

    elIsVisible = elIsVisible && el.offsetWidth > 0 && el.offsetHeight > 0;

    if(isDeep && elIsVisible) {

        while('BODY' != el.tagName && elIsVisible) {
            elIsVisible = elIsVisible && 'hidden' != window.getComputedStyle(el).visibility;
            el = el.parentElement;
        }
    }

    return elIsVisible;
}

Você pode usar o clientHeight ou propriedades clientHeight

function isViewable(element){
  return (element.clientHeight > 0);
}

element.getBoundingClientRect() tentativa. Ele irá retornar um objeto com propriedades

  • inferior
  • top
  • direito
  • esquerdo
  • largura - navegador dependente
  • altura - navegador dependente

Verifique se a largura e altura da BoundingClientRect do elemento não são zero, o que é o valor de elementos escondidos ou não-visíveis. Se os valores são maiores que zero o elemento deve ser visível no corpo. Em seguida, verifique se a propriedade bottom é inferior a screen.height o que implicaria que o elemento é withing visor. (Tecnicamente, você também tem que conta para o topo da janela do navegador, incluindo o searchbar, botões, etc.)

A resposta à primeira questão é bastante simples, se você ainda é capaz de usar protótipos (PrototypeJS):

$('HtmlElementID').visible(); returns: true|false

Aqui está uma parte da resposta que lhe diz se um elemento está na janela de visualização. Você pode precisar verificar se não há nada em cima dela usando elementFromPoint, mas é um pouco mais.

function isInViewport(element) {
  var rect = element.getBoundingClientRect();
  var windowHeight = window.innerHeight || document.documentElement.clientHeight;
  var windowWidth = window.innerWidth || document.documentElement.clientWidth;

  return rect.bottom > 0 && rect.top < windowHeight && rect.right > 0 && rect.left < windowWidth;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top