Existe-t-il un moyen efficace de savoir si un élément DOM (dans un document HTML) est actuellement visible (apparaît dans la fenêtre )?
(La question concerne Firefox.)
Existe-t-il un moyen efficace de savoir si un élément DOM (dans un document HTML) est actuellement visible (apparaît dans la fenêtre )?
(La question concerne Firefox.)
Réponses:
Mise à jour: le temps passe et nos navigateurs aussi. Cette technique n'est plus recommandée et vous devez utiliser la solution de Dan si vous n'avez pas besoin de prendre en charge la version d'Internet Explorer avant 7.
Solution originale (désormais obsolète):
Cela vérifiera si l'élément est entièrement visible dans la fenêtre courante:
function elementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top >= window.pageYOffset &&
left >= window.pageXOffset &&
(top + height) <= (window.pageYOffset + window.innerHeight) &&
(left + width) <= (window.pageXOffset + window.innerWidth)
);
}
Vous pouvez modifier cela simplement pour déterminer si une partie de l'élément est visible dans la fenêtre:
function elementInViewport2(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top < (window.pageYOffset + window.innerHeight) &&
left < (window.pageXOffset + window.innerWidth) &&
(top + height) > window.pageYOffset &&
(left + width) > window.pageXOffset
);
}
getBoundingClientRect
spécifiquement pour trouver des coordonnées d'élément ... Pourquoi ne l'utiliserions-nous pas?
Désormais, la plupart des navigateurs prennent en charge la méthode getBoundingClientRect , qui est devenue la meilleure pratique. L'utilisation d'une ancienne réponse est très lente , imprécise et comporte plusieurs bogues .
La solution choisie comme correcte n'est presque jamais précise . Vous pouvez en savoir plus sur ses bugs.
Cette solution a été testée sur Internet Explorer 7 (et versions ultérieures), iOS 5 (et versions ultérieures) Safari, Android 2.0 (Eclair) et versions ultérieures, BlackBerry, Opera Mobile et Internet Explorer Mobile 9 .
function isElementInViewport (el) {
// Special bonus for those using jQuery
if (typeof jQuery === "function" && el instanceof jQuery) {
el = el[0];
}
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */
);
}
Vous pouvez être sûr que la fonction donnée ci-dessus renvoie une réponse correcte au moment où elle est appelée, mais qu'en est-il du suivi de la visibilité de l'élément en tant qu'événement?
Placez le code suivant au bas de votre <body>
balise:
function onVisibilityChange(el, callback) {
var old_visible;
return function () {
var visible = isElementInViewport(el);
if (visible != old_visible) {
old_visible = visible;
if (typeof callback == 'function') {
callback();
}
}
}
}
var handler = onVisibilityChange(el, function() {
/* Your code go here */
});
// jQuery
$(window).on('DOMContentLoaded load resize scroll', handler);
/* // Non-jQuery
if (window.addEventListener) {
addEventListener('DOMContentLoaded', handler, false);
addEventListener('load', handler, false);
addEventListener('scroll', handler, false);
addEventListener('resize', handler, false);
} else if (window.attachEvent) {
attachEvent('onDOMContentLoaded', handler); // Internet Explorer 9+ :(
attachEvent('onload', handler);
attachEvent('onscroll', handler);
attachEvent('onresize', handler);
}
*/
Si vous faites des modifications DOM, elles peuvent bien sûr changer la visibilité de votre élément.
Lignes directrices et pièges courants:
Peut-être que vous devez suivre le zoom de la page / pincer l'appareil mobile? jQuery devrait gérer le navigateur croisé zoom / pincement , sinon le premier ou le deuxième lien devrait vous aider.
Si vous modifiez DOM , cela peut affecter la visibilité de l'élément. Vous devez prendre le contrôle de cela et appeler handler()
manuellement. Malheureusement, nous n'avons aucun onrepaint
événement multi-navigateur . D'autre part, cela nous permet de faire des optimisations et d'effectuer une nouvelle vérification uniquement sur les modifications DOM qui peuvent changer la visibilité d'un élément.
Ne l' utilisez jamais dans jQuery $ (document) .ready () uniquement, car il n'y a aucune garantie CSS a été appliquée en ce moment. Votre code peut fonctionner localement avec votre CSS sur un disque dur, mais une fois placé sur un serveur distant, il échouera.
Après le DOMContentLoaded
déclenchement, les styles sont appliqués , mais les images ne sont pas encore chargées . Donc, nous devons ajouter window.onload
un écouteur d'événements.
Nous ne pouvons pas encore capturer l'événement de zoom / pincement.
Le dernier recours pourrait être le code suivant:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Vous pouvez utiliser la page de fonctionnalités impressionnante Visibilité de l'API HTML5 si vous vous souciez si l'onglet avec votre page Web est actif et visible.
TODO: cette méthode ne gère pas deux situations:
z-index
overflow-scroll
dans le conteneur de l'élémentreturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))
) et non l'objet jQuery (par exemple isElementInViewport($("#elem))
). L'équivalent jQuery est d'ajouter [0]
comme ceci: isElementInViewport($("#elem)[0])
.
el is not defined
Dans les navigateurs modernes, vous souhaiterez peut-être consulter l' API Intersection Observer qui offre les avantages suivants:
Intersection Observer est en passe d'être une norme à part entière et est déjà pris en charge dans Chrome 51+, Edge 15+ et Firefox 55+ et est en cours de développement pour Safari. Il existe également un polyfill disponible.
Il y a quelques problèmes avec la réponse fournie par Dan qui pourraient en faire une approche inappropriée pour certaines situations. Certains de ces problèmes sont signalés dans sa réponse vers le bas, que son code donnera des faux positifs pour les éléments qui sont:
clip
propriété CSSCes limitations sont démontrées dans les résultats suivants d'un test simple :
isElementVisible()
Voici une solution à ces problèmes, avec le résultat du test ci-dessous et une explication de certaines parties du code.
function isElementVisible(el) {
var rect = el.getBoundingClientRect(),
vWidth = window.innerWidth || document.documentElement.clientWidth,
vHeight = window.innerHeight || document.documentElement.clientHeight,
efp = function (x, y) { return document.elementFromPoint(x, y) };
// Return false if it's not in the viewport
if (rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight)
return false;
// Return true if any of its four corners are visible
return (
el.contains(efp(rect.left, rect.top))
|| el.contains(efp(rect.right, rect.top))
|| el.contains(efp(rect.right, rect.bottom))
|| el.contains(efp(rect.left, rect.bottom))
);
}
Test de réussite : http://jsfiddle.net/AndyE/cAY8c/
Et le résultat:
Cette méthode n'est cependant pas sans limites. Par exemple, un élément testé avec un z-index inférieur à celui d'un autre élément au même emplacement serait identifié comme masqué même si l'élément en avant n'en cache aucune partie. Pourtant, cette méthode a ses utilisations dans certains cas que la solution de Dan ne couvre pas.
Les deux element.getBoundingClientRect()
et document.elementFromPoint()
font partie de la spécification CSSOM Working Draft et sont pris en charge dans au moins IE 6 et versions ultérieures et la plupart des navigateurs de bureau pendant une longue période (quoique pas parfaitement). Voir Quirksmode sur ces fonctions pour plus d'informations.
contains()
est utilisé pour voir si l'élément renvoyé par document.elementFromPoint()
est un nœud enfant de l'élément que nous testons pour la visibilité. Elle renvoie également true si l'élément renvoyé est le même élément. Cela rend simplement le contrôle plus robuste. Il est pris en charge dans tous les principaux navigateurs, Firefox 9.0 étant le dernier à l'ajouter. Pour une prise en charge plus ancienne de Firefox, consultez l'historique de cette réponse.
Si vous voulez tester plus de points autour de l'élément pour la visibilité, c'est-à-dire pour vous assurer que l'élément n'est pas couvert par plus de, disons, 50%, il ne faudrait pas grand-chose pour ajuster la dernière partie de la réponse. Cependant, sachez que ce serait probablement très lent si vous vérifiiez chaque pixel pour vous assurer qu'il était 100% visible.
doc
comme alias document
. Oui, j'aime à penser à cela comme une solution décente pour les cas marginaux.
element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
J'ai essayé la réponse de Dan . Cependant , l'algèbre utilisée pour déterminer les limites signifie que l'élément doit être à la fois ≤ la taille de la fenêtre d'affichage et complètement à l'intérieur de la fenêtre d'affichage true
, ce qui conduit facilement à de faux négatifs. Si vous voulez déterminer si un élément se trouve dans la fenêtre, la réponse de ryanve est proche, mais l'élément testé doit chevaucher la fenêtre, alors essayez ceci:
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
return rect.bottom > 0 &&
rect.right > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ &&
rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;
}
En tant que service public:
la réponse de Dan avec les calculs corrects (l'élément peut être> window, en particulier sur les écrans de téléphones mobiles), et les tests jQuery corrects, ainsi que l'ajout de isElementPartiallyInViewport:
Soit dit en passant, la différence entre window.innerWidth et document.documentElement.clientWidth est que clientWidth / clientHeight n'inclut pas la barre de défilement, contrairement à window.innerWidth / Height.
function isElementPartiallyInViewport(el)
{
// Special bonus for those using jQuery
if (typeof jQuery !== 'undefined' && el instanceof jQuery)
el = el[0];
var rect = el.getBoundingClientRect();
// DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
var windowWidth = (window.innerWidth || document.documentElement.clientWidth);
// http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);
return (vertInView && horInView);
}
// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el)
{
// Special bonus for those using jQuery
if (typeof jQuery !== 'undefined' && el instanceof jQuery)
el = el[0];
var rect = el.getBoundingClientRect();
var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
var windowWidth = (window.innerWidth || document.documentElement.clientWidth);
return (
(rect.left >= 0)
&& (rect.top >= 0)
&& ((rect.left + rect.width) <= windowWidth)
&& ((rect.top + rect.height) <= windowHeight)
);
}
function fnIsVis(ele)
{
var inVpFull = isElementInViewport(ele);
var inVpPartial = isElementPartiallyInViewport(ele);
console.clear();
console.log("Fully in viewport: " + inVpFull);
console.log("Partially in viewport: " + inVpPartial);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Test</title>
<!--
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="scrollMonitor.js"></script>
-->
<script type="text/javascript">
function isElementPartiallyInViewport(el)
{
// Special bonus for those using jQuery
if (typeof jQuery !== 'undefined' && el instanceof jQuery)
el = el[0];
var rect = el.getBoundingClientRect();
// DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
var windowWidth = (window.innerWidth || document.documentElement.clientWidth);
// http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);
return (vertInView && horInView);
}
// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el)
{
// Special bonus for those using jQuery
if (typeof jQuery !== 'undefined' && el instanceof jQuery)
el = el[0];
var rect = el.getBoundingClientRect();
var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
var windowWidth = (window.innerWidth || document.documentElement.clientWidth);
return (
(rect.left >= 0)
&& (rect.top >= 0)
&& ((rect.left + rect.width) <= windowWidth)
&& ((rect.top + rect.height) <= windowHeight)
);
}
function fnIsVis(ele)
{
var inVpFull = isElementInViewport(ele);
var inVpPartial = isElementPartiallyInViewport(ele);
console.clear();
console.log("Fully in viewport: " + inVpFull);
console.log("Partially in viewport: " + inVpPartial);
}
// var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft,
// var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
</script>
</head>
<body>
<div style="display: block; width: 2000px; height: 10000px; background-color: green;">
<br /><br /><br /><br /><br /><br />
<br /><br /><br /><br /><br /><br />
<br /><br /><br /><br /><br /><br />
<input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />
<br /><br /><br /><br /><br /><br />
<br /><br /><br /><br /><br /><br />
<br /><br /><br /><br /><br /><br />
<div style="background-color: crimson; display: inline-block; width: 800px; height: 500px;" ></div>
<div id="myele" onclick="fnIsVis(this);" style="display: inline-block; width: 100px; height: 100px; background-color: hotpink;">
t
</div>
<br /><br /><br /><br /><br /><br />
<br /><br /><br /><br /><br /><br />
<br /><br /><br /><br /><br /><br />
<input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />
</div>
<!--
<script type="text/javascript">
var element = document.getElementById("myele");
var watcher = scrollMonitor.create(element);
watcher.lock();
watcher.stateChange(function() {
console.log("state changed");
// $(element).toggleClass('fixed', this.isAboveViewport)
});
</script>
-->
</body>
</html>
isElementPartiallyInViewport
est également très utile. Joli.
Voir la source de verge , qui utilise getBoundingClientRect . C'est comme:
function inViewport (el) {
var r, html;
if ( !el || 1 !== el.nodeType ) { return false; }
html = document.documentElement;
r = el.getBoundingClientRect();
return ( !!r
&& r.bottom >= 0
&& r.right >= 0
&& r.top <= html.clientHeight
&& r.left <= html.clientWidth
);
}
Elle renvoie true
si une partie de l'élément se trouve dans la fenêtre.
Ma version plus courte et plus rapide:
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
Et un jsFiddle au besoin: https://jsfiddle.net/on1g619L/1/
J'ai trouvé troublant qu'il n'y ait pas de version jQuery de la fonctionnalité disponible. Lorsque j'ai découvert la solution de Dan, j'ai saisi l'opportunité de fournir quelque chose aux gens qui aiment programmer dans le style jQuery OO. C'est agréable et accrocheur et fonctionne comme un charme pour moi.
Boom Bada Bing Bada
$.fn.inView = function(){
if(!this.length)
return false;
var rect = this.get(0).getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
};
// Additional examples for other use cases
// Is true false whether an array of elements are all in view
$.fn.allInView = function(){
var all = [];
this.forEach(function(){
all.push( $(this).inView() );
});
return all.indexOf(false) === -1;
};
// Only the class elements in view
$('.some-class').filter(function(){
return $(this).inView();
});
// Only the class elements not in view
$('.some-class').filter(function(){
return !$(this).inView();
});
Usage
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// Do cool stuff
}
});
La nouvelle API Intersection Observer répond très directement à cette question.
Cette solution aura besoin d'un polyfill car Safari, Opera et Internet Explorer ne le prennent pas encore en charge (le polyfill est inclus dans la solution).
Dans cette solution, il y a une boîte hors de vue qui est la cible (observée). Lorsqu'il apparaît, le bouton en haut de l'en-tête est masqué. Il s'affiche une fois que la boîte quitte la vue.
const buttonToHide = document.querySelector('button');
const hideWhenBoxInView = new IntersectionObserver((entries) => {
if (entries[0].intersectionRatio <= 0) { // If not in view
buttonToHide.style.display = "inherit";
} else {
buttonToHide.style.display = "none";
}
});
hideWhenBoxInView.observe(document.getElementById('box'));
header {
position: fixed;
top: 0;
width: 100vw;
height: 30px;
background-color: lightgreen;
}
.wrapper {
position: relative;
margin-top: 600px;
}
#box {
position: relative;
left: 175px;
width: 150px;
height: 135px;
background-color: lightblue;
border: 2px solid;
}
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<header>
<button>NAVIGATION BUTTON TO HIDE</button>
</header>
<div class="wrapper">
<div id="box">
</div>
</div>
<!DOCTYPE html>
au HTML
IntersectionObserver
s'agit d'une fonctionnalité expérimentale (qui pourrait changer à l'avenir).
IntersectionObserver
ne déclenche que le rappel après le mouvement de la cible par rapport à la racine.
observe
est déclenché, un événement vous informe immédiatement de l'état actuel de l'intersection de l'élément suivi. Donc, d'une certaine manière - cela répond.
Toutes les réponses que j'ai rencontrées ici vérifient uniquement si l'élément est positionné à l'intérieur de la fenêtre courante . Mais cela ne signifie pas qu'il est visible .
Que se passe-t-il si l'élément donné se trouve dans une div avec un contenu débordant et qu'il est déplacé hors de la vue?
Pour résoudre ce problème, vous devez vérifier si l'élément est contenu par tous les parents.
Ma solution fait exactement cela:
Il vous permet également de spécifier la quantité de l'élément qui doit être visible.
Element.prototype.isVisible = function(percentX, percentY){
var tolerance = 0.01; //needed because the rects returned by getBoundingClientRect provide the position up to 10 decimals
if(percentX == null){
percentX = 100;
}
if(percentY == null){
percentY = 100;
}
var elementRect = this.getBoundingClientRect();
var parentRects = [];
var element = this;
while(element.parentElement != null){
parentRects.push(element.parentElement.getBoundingClientRect());
element = element.parentElement;
}
var visibleInAllParents = parentRects.every(function(parentRect){
var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
var visiblePercentageX = visiblePixelX / elementRect.width * 100;
var visiblePercentageY = visiblePixelY / elementRect.height * 100;
return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY;
});
return visibleInAllParents;
};
Cette solution a ignoré le fait que des éléments peuvent ne pas être visibles en raison d'autres faits, comme opacity: 0
.
J'ai testé cette solution dans Chrome et Internet Explorer 11.
Je trouve que la réponse acceptée ici est trop compliquée pour la plupart des cas d'utilisation. Ce code fait bien le travail (en utilisant jQuery) et différencie les éléments entièrement visibles et partiellement visibles:
var element = $("#element");
var topOfElement = element.offset().top;
var bottomOfElement = element.offset().top + element.outerHeight(true);
var $window = $(window);
$window.bind('scroll', function() {
var scrollTopPosition = $window.scrollTop()+$window.height();
var windowScrollTop = $window.scrollTop()
if (windowScrollTop > topOfElement && windowScrollTop < bottomOfElement) {
// Element is partially visible (above viewable area)
console.log("Element is partially visible (above viewable area)");
} else if (windowScrollTop > bottomOfElement && windowScrollTop > topOfElement) {
// Element is hidden (above viewable area)
console.log("Element is hidden (above viewable area)");
} else if (scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement) {
// Element is hidden (below viewable area)
console.log("Element is hidden (below viewable area)");
} else if (scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement) {
// Element is partially visible (below viewable area)
console.log("Element is partially visible (below viewable area)");
} else {
// Element is completely visible
console.log("Element is completely visible");
}
});
$window = $(window)
en cache en dehors du gestionnaire de défilement.
La solution la plus simple comme le support de Element.getBoundingClientRect () est devenue parfaite :
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
Je pense que c'est une façon plus fonctionnelle de le faire. La réponse de Dan ne fonctionne pas dans un contexte récursif.
Cette fonction résout le problème lorsque votre élément se trouve dans d'autres divs défilables en testant tous les niveaux de manière récursive jusqu'à la balise HTML et s'arrête au premier faux.
/**
* fullVisible=true only returns true if the all object rect is visible
*/
function isReallyVisible(el, fullVisible) {
if ( el.tagName == "HTML" )
return true;
var parentRect=el.parentNode.getBoundingClientRect();
var rect = arguments[2] || el.getBoundingClientRect();
return (
( fullVisible ? rect.top >= parentRect.top : rect.bottom > parentRect.top ) &&
( fullVisible ? rect.left >= parentRect.left : rect.right > parentRect.left ) &&
( fullVisible ? rect.bottom <= parentRect.bottom : rect.top < parentRect.bottom ) &&
( fullVisible ? rect.right <= parentRect.right : rect.left < parentRect.right ) &&
isReallyVisible(el.parentNode, fullVisible, rect)
);
};
Les réponses les plus acceptées ne fonctionnent pas lors d'un zoom dans Google Chrome sur Android. En combinaison avec la réponse de Dan , pour tenir compte de Chrome sur Android, visualViewport doit être utilisé. L'exemple suivant ne prend en compte que la vérification verticale et utilise jQuery pour la hauteur de la fenêtre:
var Rect = YOUR_ELEMENT.getBoundingClientRect();
var ElTop = Rect.top, ElBottom = Rect.bottom;
var WindowHeight = $(window).height();
if(window.visualViewport) {
ElTop -= window.visualViewport.offsetTop;
ElBottom -= window.visualViewport.offsetTop;
WindowHeight = window.visualViewport.height;
}
var WithinScreen = (ElTop >= 0 && ElBottom <= WindowHeight);
Voici ma solution. Cela fonctionnera si un élément est caché dans un conteneur déroulant.
Voici une démo (essayez de redimensionner la fenêtre pour)
var visibleY = function(el){
var top = el.getBoundingClientRect().top, rect, el = el.parentNode;
do {
rect = el.getBoundingClientRect();
if (top <= rect.bottom === false)
return false;
el = el.parentNode;
} while (el != document.body);
// Check it's within the document viewport
return top <= document.documentElement.clientHeight;
};
Je n'avais besoin que de vérifier si elle était visible sur l'axe Y (pour une fonction de chargement-plus-d'enregistrements Ajax).
Basé sur la solution de dan , j'ai essayé de nettoyer l'implémentation afin de pouvoir l'utiliser plusieurs fois sur la même page:
$(function() {
$(window).on('load resize scroll', function() {
addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon');
addClassToElementInViewport($('.another-thing'), 'animate-thing');
// 👏 repeat as needed ...
});
function addClassToElementInViewport(element, newClass) {
if (inViewport(element)) {
element.addClass(newClass);
}
}
function inViewport(element) {
if (typeof jQuery === "function" && element instanceof jQuery) {
element = element[0];
}
var elementBounds = element.getBoundingClientRect();
return (
elementBounds.top >= 0 &&
elementBounds.left >= 0 &&
elementBounds.bottom <= $(window).height() &&
elementBounds.right <= $(window).width()
);
}
});
La façon dont je l'utilise est que lorsque l'élément défile dans la vue, j'ajoute une classe qui déclenche une animation d'images clés CSS. C'est assez simple et fonctionne particulièrement bien lorsque vous avez plus de 10 choses à animer conditionnellement sur une page.
$window = $(window)
en cache en dehors du gestionnaire de défilement
La plupart des utilisations dans les réponses précédentes échouent à ces points:
-Lorsque n'importe quel pixel d'un élément est visible, mais pas " un coin ",
-Lorsqu'un élément est plus grand que la fenêtre et centré ,
-La plupart d'entre eux recherchent uniquement un élément singulier dans un document ou une fenêtre .
Eh bien, pour tous ces problèmes, j'ai une solution et les côtés positifs sont:
-Vous pouvez revenir
visible
quand seul un pixel de n'importe quel côté apparaît et n'est pas un coin,-Vous pouvez toujours revenir
visible
alors que l'élément est plus grand que la fenêtre,-Vous pouvez choisir votre
parent element
ou vous pouvez le laisser choisir automatiquement,-Fonctionne également sur les éléments ajoutés dynamiquement .
Si vous cochez les extraits ci-dessous, vous verrez la différence d'utilisation overflow-scroll
dans le conteneur de l'élément ne causera aucun problème et voyez que contrairement aux autres réponses ici même si un pixel apparaît de n'importe quel côté ou lorsqu'un élément est plus grand que la fenêtre et que nous voyons intérieur pixels de l'élément, il fonctionne toujours.
L'utilisation est simple:
// For checking element visibility from any sides
isVisible(element)
// For checking elements visibility in a parent you would like to check
var parent = document; // Assuming you check if 'element' inside 'document'
isVisible(element, parent)
// For checking elements visibility even if it's bigger than viewport
isVisible(element, null, true) // Without parent choice
isVisible(element, parent, true) // With parent choice
Une démonstration sans crossSearchAlgorithm
laquelle est utile pour des éléments plus grands que la fenêtre de visualisation, vérifiez les pixels intérieurs de element3 pour voir:
Vous voyez, lorsque vous êtes à l'intérieur de l'élément3, il ne parvient pas à dire s'il est visible ou non, car nous vérifions uniquement si l'élément est visible depuis les côtés ou les coins .
Et celui-ci comprend crossSearchAlgorithm
ce qui vous permet de revenir visible
quand l'élément est plus grand que la fenêtre:
JSFiddle pour jouer avec: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Ce code est fait pour des informations plus précises si une partie de l'élément est affichée dans la vue ou non. Pour les options de performances ou uniquement les diapositives verticales, ne l'utilisez pas! Ce code est plus efficace pour dessiner des cas.
Une meilleure solution:
function getViewportSize(w) {
var w = w || window;
if(w.innerWidth != null)
return {w:w.innerWidth, h:w.innerHeight};
var d = w.document;
if (document.compatMode == "CSS1Compat") {
return {
w: d.documentElement.clientWidth,
h: d.documentElement.clientHeight
};
}
return { w: d.body.clientWidth, h: d.body.clientWidth };
}
function isViewportVisible(e) {
var box = e.getBoundingClientRect();
var height = box.height || (box.bottom - box.top);
var width = box.width || (box.right - box.left);
var viewport = getViewportSize();
if(!height || !width)
return false;
if(box.top > viewport.h || box.bottom < 0)
return false;
if(box.right < 0 || box.left > viewport.w)
return false;
return true;
}
Voici une fonction qui indique si un élément est visible dans la fenêtre courante d'un élément parent :
function inParentViewport(el, pa) {
if (typeof jQuery === "function"){
if (el instanceof jQuery)
el = el[0];
if (pa instanceof jQuery)
pa = pa[0];
}
var e = el.getBoundingClientRect();
var p = pa.getBoundingClientRect();
return (
e.bottom >= p.top &&
e.right >= p.left &&
e.top <= p.bottom &&
e.left <= p.right
);
}
Ceci vérifie si un élément est au moins partiellement visible (dimension verticale):
function inView(element) {
var box = element.getBoundingClientRect();
return inViewBox(box);
}
function inViewBox(box) {
return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true;
}
function getWindowSize() {
return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight}
}
J'avais la même question et je l'ai trouvée en utilisant getBoundingClientRect ().
Ce code est complètement «générique» et ne doit être écrit qu'une seule fois pour qu'il fonctionne (vous n'avez pas besoin de l'écrire pour chaque élément que vous voulez savoir se trouve dans la fenêtre).
Ce code vérifie uniquement s'il est vertical dans la fenêtre et non horizontalement . Dans ce cas, la variable (tableau) 'elements' contient tous les éléments que vous vérifiez pour être verticalement dans la fenêtre, alors prenez tous les éléments que vous voulez n'importe où et stockez-les là.
La boucle "for" parcourt chaque élément et vérifie s'il se trouve verticalement dans la fenêtre. Ce code s'exécute à chaque fois que l'utilisateur défile! Si le getBoudingClientRect (). Top est inférieur aux 3/4 de la fenêtre d'affichage (l'élément est un quart dans la fenêtre d'affichage), il s'enregistre comme «dans la fenêtre d'affichage».
Comme le code est générique, vous voudrez savoir quel élément se trouve dans la fenêtre. Pour le découvrir, vous pouvez le déterminer par attribut personnalisé, nom de noeud, id, nom de classe, etc.
Voici mon code (dites-moi si cela ne fonctionne pas; il a été testé dans Internet Explorer 11, Firefox 40.0.3, Chrome Version 45.0.2454.85 m, Opera 31.0.1889.174 et Edge avec Windows 10, [pas encore Safari ]) ...
// Scrolling handlers...
window.onscroll = function(){
var elements = document.getElementById('whatever').getElementsByClassName('whatever');
for(var i = 0; i != elements.length; i++)
{
if(elements[i].getBoundingClientRect().top <= window.innerHeight*0.75 &&
elements[i].getBoundingClientRect().top > 0)
{
console.log(elements[i].nodeName + ' ' +
elements[i].className + ' ' +
elements[i].id +
' is in the viewport; proceed with whatever code you want to do here.');
}
};
C'est la solution simple et petite qui a fonctionné pour moi.
Exemple : vous voulez voir si l'élément est visible dans l'élément parent qui a un défilement de débordement.
$(window).on('scroll', function () {
var container = $('#sidebar');
var containerHeight = container.height();
var scrollPosition = $('#row1').offset().top - container.offset().top;
if (containerHeight < scrollPosition) {
console.log('not visible');
} else {
console.log('visible');
}
})
Toutes les réponses ici déterminent si l'élément est entièrement contenu dans la fenêtre, pas seulement visible d'une manière ou d'une autre. Par exemple, si seulement la moitié d'une image est visible en bas de la vue, les solutions ici échoueront, considérant que "l'extérieur".
J'ai eu un cas d'utilisation où je fais du chargement paresseux via IntersectionObserver
, mais en raison des animations qui se produisent pendant la pop-in, je ne voulais pas observer d'images qui étaient déjà coupées lors du chargement de la page. Pour ce faire, j'ai utilisé le code suivant:
const bounding = el.getBoundingClientRect();
const isVisible = (0 < bounding.top && bounding.top < (window.innerHeight || document.documentElement.clientHeight)) ||
(0 < bounding.bottom && bounding.bottom < (window.innerHeight || document.documentElement.clientHeight));
Il s'agit essentiellement de vérifier si la limite supérieure ou inférieure se trouve indépendamment dans la fenêtre. L'extrémité opposée peut être à l'extérieur, mais tant qu'une extrémité est à l'intérieur, elle est "visible" au moins partiellement.
J'utilise cette fonction (elle vérifie uniquement si le y est en écran car la plupart du temps le x n'est pas nécessaire)
function elementInViewport(el) {
var elinfo = {
"top":el.offsetTop,
"height":el.offsetHeight,
};
if (elinfo.top + elinfo.height < window.pageYOffset || elinfo.top > window.pageYOffset + window.innerHeight) {
return false;
} else {
return true;
}
}
Pour un défi similaire, j'ai vraiment apprécié cet essentiel qui expose un polyfill pour scrollIntoViewIfNeeded () .
Tout le Kung Fu nécessaire pour répondre est dans ce bloc:
var parent = this.parentNode,
parentComputedStyle = window.getComputedStyle(parent, null),
parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
alignWithTop = overTop && !overBottom;
this
fait référence à l'élément que vous voulez savoir s'il s'agit, par exemple, overTop
ou overBottom
- vous devriez juste obtenir la dérive ...