¿Cómo puedo verificar si un elemento es realmente visible con JavaScript?

En JavaScript, ¿cómo verificaría si un elemento es realmente visible?

No solo me refiero a verificar la visibility y display attributes de display . Quiero decir, verificar que el elemento no sea

  • visibility: hidden o display: none
  • debajo de otro elemento
  • desplazado por el borde de la pantalla

Por razones técnicas, no puedo include ningún script. Sin embargo, puedo usar Prototype tal como está en la página.

Para el punto 2.

Veo que nadie ha sugerido usar document.elementFromPoint(x,y) , para mí es la manera más rápida de probar si un elemento está nested u oculto por otro. Puede pasar las compensaciones del elemento objective a la function.

Aquí está la página de testing de PPK en elementFromPoint .

No sé cuánto de esto es compatible con browseres antiguos o no tan modernos, pero estoy usando algo como esto (sin necesidad de ninguna biblioteca):

 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; 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; } 

Comtesting que el elemento tiene un área> 0 y luego comtesting si alguna parte del elemento está dentro de la window gráfica y que no está oculta "debajo" de otro elemento (en realidad solo verifico un punto único en el centro del elemento , por lo que no está 100% garantizado, pero podría modificar la secuencia de commands para iterar sobre todos los puntos del elemento, si realmente necesita …).

Actualizar

Función modificada on_top que verifica cada píxel:

 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; }; 

No sé sobre el performance 🙂

Como señaló jkl, comprobar la visibilidad o visualización del elemento no es suficiente. Tienes que verificar sus antepasados. El selenium hace esto cuando verifica la visibilidad en un elemento.

Consulte el método Selenium.prototype.isVisible en el file selenium-api.js.

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

Interesante pregunta.

Este sería mi enfoque.

  1. En primer lugar, compruebe que element.style.visibility! == 'hidden' && element.style.display! == 'none'
  2. Luego pruebe con document.elementFromPoint (element.offsetLeft, element.offsetTop) si el elemento devuelto es el elemento que espero, esto es difícil de detectar si un elemento se superpone por completo.
  3. Finalmente pruebe si offsetTop y offsetLeft están ubicados en la window gráfica teniendo en count las compensaciones de desplazamiento.

Espero eso ayude.

Esto es lo que tengo hasta ahora. Cubre tanto 1 como 3. Sin embargo, sigo luchando con 2 ya que no estoy familiarizado con Prototype (soy más un tipo de jQuery).

 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; } 

La biblioteca de elementos de Prototype es una de las bibliotecas de consulta más potentes en cuanto a los methods. Te recomiendo que revises la API.

Algunos consejos:

  1. Verificar la visibilidad puede ser Element.getStyle() , pero puede usar el método Element.visible() methods Element.visible() combinados en una function personalizada. Con getStyle() puede verificar el estilo calculado real.

  2. No sé exactamente a qué te refieres con "debajo" 🙂 Si quieres decir que tiene un ancestro específico, por ejemplo, un div envoltorio, puedes usar Element.up(cssRule) :

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

    Si desea verificar los elementos secundarios del elemento secundario, puede hacerlo también:

     var child = $("myparagraph"); if(!child.previous("mywrapper")){ // I lost my bro! } else { // I found my bro! } 
  3. De nuevo, Element lib puede ayudarlo si entiendo correctamente lo que quiere decir 🙂 Puede verificar las dimensiones reales de la window gráfica y el desplazamiento de su elemento para que pueda calcular si su elemento está "fuera de pantalla".

¡Buena suerte!

Pegué un caso de testing para prototypejs en http://gist.github.com/117125 . Parece que en tu caso simplemente no podemos confiar en getStyle() en absoluto. Para maximizar la confiabilidad de la function isMyElementReallyVisible, debe combinar lo siguiente:

  • Comprobando el estilo calculado (Dojo tiene una buena implementación que puedes tomar prestada)
  • Comprobación del viewoffoffset (prototipo de método nativo)
  • Comprobando el índice z para el problema "debajo" (en Internet Explorer puede tener errores)

Una forma de hacerlo es:

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

Créditos: https://github.com/atetlaw/Really-Easy-Field-Validation/blob/master/validation.js#L178

Captura los events mouse-drag y viewport (onmouseup, onresize, onscroll).

Cuando finalice un arrastre haga una comparación del límite del elemento arrastrado con todos los "elementos de interés" (es decir, los elementos con la class "dont_hide" o una matriz de identificadores). Haga lo mismo con window.onscroll y window.onresize. Marque cualquier elemento oculto con un atributo especial o nombre de class o simplemente realice cualquier acción que desee en ese momento.

Las testings ocultas son bastante fáciles. Para "totalmente oculto", desea saber si TODAS las esquinas están dentro del límite del elemento arrastrado o fuera de la window gráfica. Para parcialmente oculto, está buscando una esquina única que coincida con la misma testing.

No creo que verificar la visibilidad y las properties de visualización del elemento sea lo suficientemente bueno para el requisito n. ° 1, incluso si usa currentStyle / getComputedStyle. También debes verificar los antepasados ​​del elemento. Si un antepasado está oculto, también lo está el elemento.

No quisiera networkingirigirlo a jQuery (como suele hacerse), pero esta discusión sobre cuándo los elementos son realmente visibles es muy perspicaz.

Y desde jQuery 1.3.2 esto ya no es un problema .

Verifique los elementos 'propiedad offsetHeight. Si es más de 0, es visible. Nota: este enfoque no cubre una situación cuando se establece visibilidad: estilo oculto. Pero ese estilo es algo extraño de todos modos.

Aquí hay un script de muestra y un caso de testing. Cubiertas de elementos posicionados, visibilidad: oculta, pantalla: ninguna. No probó el índice Z, suponga 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 networking; } 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; } 

Puede usar las properties clientHeight o clientWidth

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

Pruebe element.getBoundingClientRect() . Devolverá un object con properties

  • background
  • parte superior
  • derecho
  • izquierda
  • ancho – dependiente del browser
  • altura – dependiente del browser

Compruebe que el ancho y el alto del BoundingClientRect del elemento no sean cero, que es el valor de elementos ocultos o no visibles. Si los valores son mayores que cero, el elemento debe ser visible en el cuerpo. A continuación, compruebe si la propiedad bottom es inferior a screen.height lo que implicaría que el elemento está dentro de la window gráfica. (Técnicamente, también debería tener en count la parte superior de la window del browser, incluida la barra de búsqueda, los botones, etc.)

Aquí hay una parte de la respuesta que le dice si un elemento está en la window gráfica. Es posible que deba verificar si no hay nada encima utilizando elementFromPoint, pero es un poco más largo.

 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 } 

La respuesta al primer punto es bastante simple si todavía puedes usar el prototipo (prototypejs):

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