Czy istnieje skuteczny sposób stwierdzenia, czy element DOM (w dokumencie HTML) jest obecnie widoczny (pojawia się w rzutni )?
(Pytanie dotyczy przeglądarki Firefox).
Czy istnieje skuteczny sposób stwierdzenia, czy element DOM (w dokumencie HTML) jest obecnie widoczny (pojawia się w rzutni )?
(Pytanie dotyczy przeglądarki Firefox).
Odpowiedzi:
Aktualizacja: czas mija, podobnie jak nasze przeglądarki. Ta technika nie jest już zalecana i powinieneś skorzystać z rozwiązania Dana, jeśli nie potrzebujesz obsługiwać wersji Internet Explorera przed 7.
Oryginalne rozwiązanie (teraz nieaktualne):
Spowoduje to sprawdzenie, czy element jest całkowicie widoczny w bieżącej rzutni:
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)
);
}
Możesz to zmodyfikować, aby określić, czy jakaś część elementu jest widoczna w rzutni:
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
w celu znalezienia współrzędnych elementów ... Dlaczego nie mielibyśmy go użyć?
Teraz większość przeglądarek obsługuje metodę getBoundingClientRect , która stała się najlepszą praktyką. Użycie starej odpowiedzi jest bardzo wolne , niedokładne i zawiera kilka błędów .
Rozwiązanie wybrane jako prawidłowe prawie nigdy nie jest precyzyjne . Możesz przeczytać więcej o jego błędach.
To rozwiązanie zostało przetestowane na Internet Explorerze 7 (i późniejszych), iOS 5 (i późniejszych) Safari, Android 2.0 (Eclair) i późniejszych, BlackBerry, Opera Mobile i 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() */
);
}
Możesz być pewien, że podana powyżej funkcja zwraca poprawną odpowiedź w momencie jej wywołania, ale co z widocznością elementu śledzącego jako zdarzenia?
Umieść następujący kod na dole <body>
tagu:
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);
}
*/
Jeśli dokonasz jakichkolwiek modyfikacji DOM, mogą one oczywiście zmienić widoczność Twojego elementu.
Wytyczne i typowe pułapki:
Może potrzebujesz śledzić powiększenie strony / uszczypnięcie urządzenia mobilnego? jQuery powinien obsługiwać przeglądarkę Zoom / Pinch w różnych przeglądarkach, w przeciwnym razie pierwszy lub drugi link powinien ci pomóc.
Jeśli zmodyfikujesz DOM , może to wpłynąć na widoczność elementu. Powinieneś przejąć nad tym kontrolę i zadzwonić handler()
ręcznie. Niestety nie mamy żadnych onrepaint
wydarzeń w różnych przeglądarkach . Z drugiej strony pozwala nam to na optymalizacje i ponowne sprawdzenie tylko tych modyfikacji DOM, które mogą zmienić widoczność elementu.
Nigdy nie używaj go tylko w jQuery $ (dokument) .ready () , ponieważ w tej chwili nie ma gwarancji CSS . Twój kod może współpracować lokalnie z CSS na dysku twardym, ale po umieszczeniu na zdalnym serwerze zawiedzie.
Po DOMContentLoaded
wystrzeleniu, style są stosowane , ale zdjęcia nie są jeszcze załadowany . Powinniśmy dodać window.onload
detektor zdarzeń.
Nie możemy jeszcze złapać zdarzenia powiększenia / uszczypnięcia.
Ostatnim rozwiązaniem może być następujący kod:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Możesz skorzystać z niesamowitej strony funkcji Visiiliili interfejsu API HTML5, jeśli zależy Ci na tym, czy karta z twoją stroną jest aktywna i widoczna.
DO ZROBIENIA: ta metoda nie obsługuje dwóch sytuacji:
z-index
overflow-scroll
w kontenerze elementureturn (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
isElementInViewport(document.getElementById('elem'))
), A nie obiekt jQuery (np isElementInViewport($("#elem))
.). Równowartość jQuery jest dodanie [0]
tak: isElementInViewport($("#elem)[0])
.
el is not defined
W nowoczesnych przeglądarkach warto sprawdzić interfejs API Intersection Observer, który zapewnia następujące korzyści:
Intersection Observer jest na dobrej drodze, aby stać się pełnoprawnym standardem i jest już obsługiwany w Chrome 51+, Edge 15+ i Firefox 55+ i jest w fazie rozwoju dla Safari. Dostępna jest również polifill .
Istnieją pewne problemy z odpowiedzią udzieloną przez Dana, które mogą sprawić, że będzie to nieodpowiednie podejście w niektórych sytuacjach. Niektóre z tych problemów są wskazane w jego odpowiedzi u dołu, że jego kod da fałszywe trafienia dla elementów, które są:
clip
właściwości CSSOgraniczenia te zostały przedstawione w następujących wynikach prostego testu :
isElementVisible()
Oto rozwiązanie tych problemów, z poniższym wynikiem testu i objaśnieniem niektórych części kodu.
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))
);
}
Zaliczenie testu: http://jsfiddle.net/AndyE/cAY8c/
A wynik:
Ta metoda nie jest jednak pozbawiona własnych ograniczeń. Na przykład element testowany z niższym indeksem Z niż inny element w tym samym miejscu zostałby zidentyfikowany jako ukryty, nawet jeśli element z przodu faktycznie nie ukrywa żadnej jego części. Mimo to metoda ta ma zastosowanie w niektórych przypadkach, których rozwiązanie Dana nie obejmuje.
Zarówno element.getBoundingClientRect()
i document.elementFromPoint()
są częścią projektu specyfikacji CSSOM roboczej i są obsługiwane w co najmniej IE 6 i nowszych, a większość przeglądarek pulpitu przez długi czas (choć nie idealnie). Zobacz Quirksmode na temat tych funkcji, aby uzyskać więcej informacji.
contains()
służy do sprawdzenia, czy zwracany element document.elementFromPoint()
jest węzłem potomnym elementu, który testujemy pod kątem widoczności. Zwraca również wartość true, jeśli zwracany element jest tym samym elementem. Dzięki temu czek jest bardziej niezawodny. Jest obsługiwany we wszystkich głównych przeglądarkach, a Firefox 9.0 jest ostatnim z nich, aby go dodać. Aby uzyskać starszą obsługę Firefoksa, sprawdź historię tej odpowiedzi.
Jeśli chcesz przetestować więcej punktów wokół elementu pod kątem widoczności ―, tj. Aby upewnić się, że element nie jest objęty więcej niż, powiedzmy, 50% ―, nie zajęłoby wiele, aby dopasować ostatnią część odpowiedzi. Pamiętaj jednak, że prawdopodobnie sprawdziłby się każdy piksel, aby upewnić się, że jest w 100% widoczny.
doc
jako aliasu document
. Tak, lubię myśleć o tym jako o przyzwoitym rozwiązaniu w przypadku skrzynek.
element.contains(efp(rect.right - (rect.width / 2), rect.bottom - (rect.height / 2)))
Próbowałem odpowiedzi Dana . Jednak algebra zastosowana do określenia granic oznacza, że element musi być zarówno ≤ wielkości rzutni, jak i całkowicie wewnątrz rzutni, aby uzyskać true
, co łatwo prowadzi do fałszywych negatywów. Jeśli chcesz ustalić, czy element jest w ogóle w rzutni, odpowiedź Ryanve jest bliska, ale testowany element powinien nakładać się na rzutnię, więc spróbuj tego:
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() */;
}
Jako usługa publiczna:
odpowiedź Dana z poprawnymi obliczeniami (elementem może być> okno, szczególnie na ekranach telefonów komórkowych) i poprawne testowanie jQuery, a także dodanie isElementPartIALInViewport:
Nawiasem mówiąc, różnica między window.innerWidth i document.documentElement.clientWidth polega na tym, że clientWidth / clientHeight nie zawiera paska przewijania, podczas gdy 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
jest również bardzo przydatny. Niezłe.
Zobacz źródło verge , które korzysta z getBoundingClientRect . To jest jak:
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
);
}
Zwraca, true
jeśli jakaś część elementu znajduje się w rzutni.
Moja krótsza i szybsza wersja:
function isElementOutViewport(el){
var rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}
I jsFiddle zgodnie z wymaganiami: https://jsfiddle.net/on1g619L/1/
Niepokoiło mnie to, że nie było dostępnej dla jQuery wersji funkcjonalności. Kiedy natknąłem się na rozwiązanie Dana, zauważyłem możliwość zapewnienia czegoś dla ludzi, którzy lubią programować w stylu jQuery OO. Jest miły i zgryźliwy i działa jak urok dla mnie.
Bada bing bada boom
$.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();
});
Stosowanie
$(window).on('scroll',function(){
if( $('footer').inView() ) {
// Do cool stuff
}
});
Nowy interfejs API Intersection Observer rozwiązuje to pytanie bardzo bezpośrednio.
To rozwiązanie będzie wymagało wypełniania polifill, ponieważ Safari, Opera i Internet Explorer nie obsługują tego jeszcze (wypełnienie jest zawarte w rozwiązaniu).
W tym rozwiązaniu pole jest poza polem widzenia, które jest celem (obserwowanym). Jeśli chodzi o widok, przycisk u góry nagłówka jest ukryty. Jest pokazywany, gdy pudełko opuści widok.
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>
do HTML
IntersectionObserver
jest to funkcja eksperymentalna (która może ulec zmianie w przyszłości).
IntersectionObserver
wyzwala tylko oddzwonienie po ruchu celu względem korzenia.
observe
zdarzenia natychmiast zostaje wyzwolony komunikat informujący o bieżącym stanie przecięcia śledzonego elementu. Tak więc, w jakiś sposób - dotyczy.
Wszystkie odpowiedzi, które tu napotkałem, sprawdzają tylko, czy element jest umieszczony w bieżącej rzutni . Ale to nie znaczy, że jest widoczne .
Co jeśli dany element znajduje się w div z przepełnioną zawartością i jest przewijany poza pole widzenia?
Aby rozwiązać ten problem, musisz sprawdzić, czy element jest zawarty przez wszystkich rodziców.
Moje rozwiązanie robi dokładnie to:
Pozwala także określić, jak duży element ma być widoczny.
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;
};
To rozwiązanie zignorowało fakt, że elementy mogą nie być widoczne z powodu innych faktów, takich jak opacity: 0
.
Przetestowałem to rozwiązanie w Chrome i Internet Explorer 11.
Uważam, że przyjęta tutaj odpowiedź jest zbyt skomplikowana w przypadku większości przypadków użycia. Ten kod dobrze wykonuje zadanie (używając jQuery) i rozróżnia elementy w pełni widoczne i częściowo widoczne:
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)
poza modułem przewijania.
Najprostsze rozwiązanie jako obsługa elementu Element.getBoundingClientRect () stało się idealne :
function isInView(el) {
let box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
Myślę, że jest to bardziej funkcjonalny sposób na zrobienie tego. Odpowiedź Dana nie działa w kontekście rekurencyjnym.
Ta funkcja rozwiązuje problem, gdy element znajduje się wewnątrz innych przewijalnych div div, testując rekursywnie wszystkie poziomy aż do znacznika HTML, i zatrzymuje się przy pierwszym false.
/**
* 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)
);
};
Najczęściej akceptowane odpowiedzi nie działają podczas powiększania w Google Chrome na Androida. W połączeniu z odpowiedzią Dana , aby uwzględnić Chrome na Androida, należy użyć visualViewport . Poniższy przykład uwzględnia tylko sprawdzenie pionowe i używa jQuery dla wysokości okna:
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);
Oto moje rozwiązanie. Działa, jeśli element jest ukryty w przewijalnym pojemniku.
Oto demo (spróbuj zmienić rozmiar okna)
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;
};
Musiałem tylko sprawdzić, czy jest widoczny na osi Y (dla przewijanej funkcji ładowania więcej rekordów Ajax).
Opierając się na rozwiązaniu dana , postanowiłem wyczyścić implementację, aby korzystanie z niej wielokrotnie na tej samej stronie było łatwiejsze:
$(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()
);
}
});
Używam go w ten sposób, że gdy element przewija się do widoku, dodaję klasę, która wyzwala animację klatki kluczowej CSS. Jest to dość proste i działa szczególnie dobrze, gdy masz ponad 10 rzeczy warunkowo animowanych na stronie.
$window = $(window)
poza
Większość zwyczajów z poprzednich odpowiedzi kończy się niepowodzeniem w następujących punktach:
- Gdy widoczny jest dowolny piksel elementu, ale nie „ narożnik ”,
-Gdy element jest większy niż rzutnia i wyśrodkowany ,
-Większość z nich sprawdza tylko pojedynczy element w dokumencie lub oknie .
Cóż, dla wszystkich tych problemów mam rozwiązanie, a plusy to:
-Możesz powrócić,
visible
gdy pojawi się tylko piksel z dowolnej strony i nie jest to narożnik,- Nadal możesz powrócić,
visible
gdy element jest większy niż rzutnia,-Możesz wybrać swój
parent element
lub możesz automatycznie pozwolić mu wybrać,- Działa również na dynamicznie dodawanych elementach .
Jeśli sprawdzisz poniższe fragmenty, zobaczysz różnicę w użyciu overflow-scroll
w kontenerze elementu nie spowoduje żadnych problemów i zobaczysz, że w przeciwieństwie do innych odpowiedzi tutaj, nawet jeśli piksel pojawia się z dowolnej strony lub gdy element jest większy niż obszar wyświetlania i widzimy wnętrze piksele elementu, który nadal działa.
Użycie jest proste:
// 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
Demonstracja, bez crossSearchAlgorithm
której jest przydatna dla elementów większych niż element kontrolny rzutni 3 wewnętrzne piksele, aby zobaczyć:
Widzisz, gdy jesteś w elemencie3 , nie określa , czy jest widoczny, czy nie, ponieważ sprawdzamy tylko, czy element jest widoczny z boku lub z narożników .
I ta zawiera, crossSearchAlgorithm
która pozwala ci wrócić, visible
gdy element jest większy niż rzutnia:
JSFiddle do zabawy z: http://jsfiddle.net/BerkerYuceer/grk5az2c/
Ten kod ma na celu uzyskanie dokładniejszych informacji, jeśli jakaś część elementu jest pokazana w widoku, czy nie. Aby korzystać z opcji wydajności lub tylko pionowych slajdów, nie używaj tego! Ten kod jest bardziej skuteczny w rysowaniu przypadków.
Lepsze rozwiązanie:
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;
}
Oto funkcja, która informuje, czy element jest widoczny w bieżącej rzutni elementu nadrzędnego :
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
);
}
Sprawdza, czy element jest przynajmniej częściowo widoczny (wymiar pionowy):
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}
}
Miałem to samo pytanie i rozgryzłem je za pomocą getBoundingClientRect ().
Ten kod jest całkowicie „ogólny” i musi być napisany tylko raz, aby zadziałał (nie musisz go pisać dla każdego elementu, który chcesz wiedzieć, jest w okienku ekranu).
Ten kod sprawdza tylko, czy jest pionowo w rzutni, a nie w poziomie . W tym przypadku „elementy” zmiennej (tablicy) przechowują wszystkie elementy, które sprawdzasz, aby były pionowo w rzutni, więc chwyć dowolne elementy w dowolnym miejscu i przechowuj je tam.
„Pętla for” zapętla każdy element i sprawdza, czy znajduje się on pionowo w rzutni. Ten kod jest wykonywany za każdym razem, gdy użytkownik przewija! Jeśli getBoudingClientRect (). Top ma mniej niż 3/4 rzutni (element ma jedną czwartą w rzutni), rejestruje się jako „w rzutni”.
Ponieważ kod jest ogólny, będziesz chciał wiedzieć, który element znajduje się w rzutni. Aby się tego dowiedzieć, możesz to ustalić na podstawie niestandardowego atrybutu, nazwy węzła, identyfikatora, nazwy klasy i innych kryteriów.
Oto mój kod (powiedz mi, jeśli nie działa; został przetestowany w Internet Explorer 11, Firefox 40.0.3, Chrome Wersja 45.0.2454.85 m, Opera 31.0.1889.174 i Edge z Windows 10, [jeszcze nie 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.');
}
};
To proste i małe rozwiązanie, które zadziałało dla mnie.
Przykład : Chcesz sprawdzić, czy element jest widoczny w elemencie nadrzędnym, który ma przewijanie przepełnienia.
$(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');
}
})
Wszystkie odpowiedzi tutaj określają, czy element jest całkowicie zawarty w rzutni, a nie tylko w jakiś sposób widoczny. Na przykład, jeśli tylko połowa obrazu jest widoczna u dołu widoku, rozwiązania tutaj zawiodą, biorąc pod uwagę, że „na zewnątrz”.
Miałem przypadek użycia, w którym ładuję się leniwie IntersectionObserver
, ale z powodu animacji pojawiających się podczas wyskakującego okienka nie chciałem oglądać żadnych obrazów, które były już przecinane podczas ładowania strony. Aby to zrobić, użyłem następującego kodu:
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));
Jest to w zasadzie sprawdzenie, czy górna lub dolna granica jest niezależnie w rzutni. Przeciwny koniec może znajdować się na zewnątrz, ale dopóki jeden jest w środku, jest „widoczny” co najmniej częściowo.
Korzystam z tej funkcji (sprawdza tylko, czy y jest niewidoczne, ponieważ przez większość czasu x nie jest potrzebne)
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;
}
}
W przypadku podobnego wyzwania bardzo podobała mi się ta treść, która ujawnia wypełnienie dla scrollIntoViewIfNeeded () .
Wszystkie niezbędne Kung Fu potrzebne do odpowiedzi znajdują się w tym bloku:
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
odnosi się do elementu, który chcesz wiedzieć, jeśli jest, na przykład, overTop
lub overBottom
- powinieneś dostać dryf ...