En JavaScript, comment vérifier si un élément est réellement visible?
Je ne parle pas seulement de vérifier les attributs visibility
et display
. Je veux dire, vérifier que l'élément n'est pas
visibility: hidden
ou display: none
Pour des raisons techniques, je ne peux inclure aucun script. Je peux cependant utiliser Prototype tel qu'il est déjà sur la page.
Pour le point 2.
Je vois que personne n’a suggéré d’utiliser document.elementFromPoint(x,y)
, c’est le moyen le plus rapide de tester si un élément est imbriqué ou caché par un autre. Vous pouvez transmettre les décalages de l'élément ciblé à la fonction.
Voici la page de test PPK sur elementFromPoint .
Je ne sais pas dans quelle mesure cela est pris en charge par les navigateurs plus anciens ou moins modernes, mais j'utilise quelque chose comme ceci (sans besoin de librairies):
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;
}
Il vérifie que l'élément a une surface> 0, puis vérifie si une partie de l'élément se trouve dans la fenêtre et qu'elle n'est pas masquée "sous" un autre élément (en fait, je ne vérifie qu'un seul point au centre de l'élément de sorte que ce n’est pas sûr à 100% - mais vous pouvez simplement modifier le script pour parcourir tous les points de l’élément, si vous en avez vraiment besoin ...).
Mise à jour
Fonction on_top modifiée vérifiant chaque 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;
};
Je ne sais pas pour la performance :)
Comme l'a souligné jkl, vérifier la visibilité ou l'affichage de l'élément ne suffit pas. Vous devez vérifier ses ancêtres. Sélénium fait cela lorsqu'il vérifie la visibilité d'un élément.
Découvrez la méthode Selenium.prototype.isVisible dans le fichier Selenium-api.js.
http://svn.openqa.org/svn/Selenium-on-Rails/Selenium-on-Rails/Selenium-core/scripts/Selenium-api.js
C'est ce que j'ai jusqu'ici. Il couvre à la fois 1 et 3. Je suis toutefois toujours aux prises avec 2 puisque je ne suis pas familier avec Prototype (je suis plus du type 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;
}
Question interessante.
Ce serait mon approche.
J'espère que ça aide.
Le prototype Element library est l'une des bibliothèques de requêtes les plus puissantes en termes de méthodes. Je vous recommande de vérifier l'API.
Quelques astuces:
Vérifier la visibilité peut être un problème, mais vous pouvez utiliser les méthodes Element.getStyle()
et Element.visible()
combinées dans une fonction personnalisée. Avec getStyle()
, vous pouvez vérifier le style calculé.
Je ne sais pas exactement ce que vous entendez par "dessous" :) Si vous entendez par là a un ancêtre spécifique, par exemple un div wrapper, vous pouvez utiliser Element.up(cssRule)
:
var child = $("myparagraph");
if(!child.up("mywrapper")){
// I lost my mom!
}
else {
// I found my mom!
}
Si vous souhaitez vérifier les frères et soeurs de l'élément enfant, vous pouvez également le faire:
var child = $("myparagraph");
if(!child.previous("mywrapper")){
// I lost my bro!
}
else {
// I found my bro!
}
Encore une fois, Element lib peut vous aider si je comprends bien ce que vous voulez dire :) Vous pouvez vérifier le réel dimensions de la fenêtre et le décalage de votre élément afin de pouvoir calculer si votre élément est "hors écran".
Bonne chance!
J'ai collé un scénario de test pour prototypejs à l'adresse http://Gist.github.com/117125 . Il semble que dans votre cas, nous ne pouvons tout simplement pas faire confiance à getStyle()
du tout. Pour optimiser la fiabilité de la fonction isMyElementReallyVisible, vous devez combiner les éléments suivants:
Une façon de le faire est:
isVisible(Elm) {
while(Elm.tagName != 'BODY') {
if(!$(Elm).visible()) return false;
Elm = Elm.parentNode;
}
return true;
}
Crédits: https://github.com/atetlaw/Really-Easy-Field-Validation/blob/master/validation.js#L178
Je détesterais vous rediriger vers jQuery (comme souvent), mais cette discussion quand les éléments sont vraiment visibles est très perspicace.
Et depuis jQuery 1.3.2 c'est plus un problème .
Je ne pense pas que la vérification de la visibilité et des propriétés d'affichage de l'élément soit suffisante pour l'exigence n ° 1, même si vous utilisez currentStyle/getComputedStyle. Vous devez également vérifier les ancêtres de l'élément. Si un ancêtre est masqué, l'élément l'est également.
Attrapez les événements de glisser-déposer et d'affichage (onmouseup, onresize, onscroll).
Lorsque le glissement prend fin, faites une comparaison de la limite de l'élément glissé avec tous les "éléments d'intérêt" (c'est-à-dire, les éléments avec la classe "dont_hide" ou un tableau d'identifiants). Faites la même chose avec window.onscroll et window.onresize. Marquez tous les éléments cachés avec un attribut spécial ou un nom de classe ou effectuez simplement l'action que vous voulez à ce moment-là.
Les tests cachés sont assez faciles. Pour "totalement caché", vous voulez savoir si TOUS les coins sont situés à l'intérieur des limites de l'élément déplacé ou à l'extérieur de la fenêtre. Pour partiellement masqué, vous recherchez un seul coin correspondant au même test.
Vérifiez la propriété offsetHeight des éléments. S'il est supérieur à 0, il est visible. Remarque: cette approche ne couvre pas une situation où visibilité: le style caché est défini. Mais ce style est quand même bizarre.
Essayez element.getBoundingClientRect()
. Il retournera un objet avec des propriétés
Vérifiez que la largeur et la hauteur de l'élément BoundingClientRect
de l'élément ne sont pas nulles, ce qui correspond à la valeur des éléments masqués ou non visibles. Si les valeurs sont supérieures à zéro, l'élément doit être visible dans le corps. Puis vérifiez si la propriété bottom
est inférieure à screen.height
_ ce qui impliquerait que l’élément se trouve dans la fenêtre de visualisation. (Techniquement, vous devez également prendre en compte le haut de la fenêtre du navigateur, barre de recherche, boutons, etc.).
/**
* 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 <[email protected]>
*/
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;
}
Voici un exemple de script et de scénario de test. Couvre les éléments positionnés, visibilité: masquée, affichage: aucune. N'a pas testé z-index, supposons que cela fonctionne.
<!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>
Vous pouvez utiliser les propriétés clientHeight ou clientWidth
function isViewable(element){
return (element.clientHeight > 0);
}