Gibt es eine effiziente Methode, um festzustellen, ob ein DOM-Element (in einem HTML-Dokument) derzeit sichtbar ist (erscheint in der Ansichtsfenster )?
(Die Frage bezieht sich auf Firefox.)
Gibt es eine effiziente Methode, um festzustellen, ob ein DOM-Element (in einem HTML-Dokument) derzeit sichtbar ist (erscheint in der Ansichtsfenster )?
(Die Frage bezieht sich auf Firefox.)
Jetzt die meisten Browser Unterstützung getBoundingClientRect Methode, die sich zur besten Praxis entwickelt hat. Die Verwendung einer alten Antwort ist sehr langsam, nicht korrekt y hat mehrere Bugs .
Die als richtig ausgewählte Lösung lautet fast nie präzise .
Diese Lösung wurde mit Internet Explorer 7 (und höher), iOS 5 (und höher) und Safari getestet, Android 2.0 (Eclair) und höher, BlackBerry, Opera Mobile und 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() */
);
}
Sie können sicher sein, dass die oben angegebene Funktion zum Zeitpunkt ihres Aufrufs eine korrekte Antwort liefert, aber was ist mit der Verfolgung der Sichtbarkeit des Elements als Ereignis?
Fügen Sie den folgenden Code am Ende Ihrer <body>
Tag:
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);
}
*/
Wenn Sie irgendwelche DOM-Änderungen vornehmen, können diese natürlich die Sichtbarkeit Ihres Elements verändern.
Leitlinien und häufige Fallstricke:
Vielleicht müssen Sie den Seitenzoom / das Einklemmen von Mobilgeräten verfolgen? jQuery sollte behandeln vergrößern/verkleinern browserübergreifend, sonst erste o zweite Link sollte Ihnen helfen.
Wenn Sie DOM ändern kann sie die Sichtbarkeit des Elements beeinträchtigen. Sie sollten die Kontrolle darüber übernehmen und die handler()
manuell. Leider haben wir keine browserübergreifenden onrepaint
Ereignis. Andererseits können wir so Optimierungen vornehmen und nur bei DOM-Änderungen, die die Sichtbarkeit eines Elements verändern können, eine erneute Prüfung durchführen.
Niemals innerhalb von jQuery verwenden $(document).ready() nur, weil es wurde keine Garantie CSS angewendet in diesem Moment. Ihr Code kann lokal mit Ihrem CSS auf einer Festplatte funktionieren, aber sobald er auf einem entfernten Server liegt, wird er fehlschlagen.
Nach DOMContentLoaded
abgefeuert wird, Stile angewendet werden pero die Bilder sind noch nicht geladen . Wir sollten also hinzufügen window.onload
Ereignishörer.
Wir können das Ereignis Zoom/Pinch noch nicht erfassen.
Der letzte Ausweg könnte der folgende Code sein:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
Sie können die großartige Funktion nutzen pageVisibiliy der HTML5-API, wenn Sie sich dafür interessieren, ob die Registerkarte mit Ihrer Webseite aktiv und sichtbar ist.
TODO: Diese Methode kann zwei Situationen nicht behandeln:
z-index
.overflow-scroll
im Container des Elements.
Ich verwende diese Lösung (achten Sie allerdings auf den Tippfehler "botom"). Es gibt auch etwas zu beachten, wenn das Element, das wir in Betracht ziehen, Bilder in ihm haben würde. Chrome (zumindest) muss warten, bis das Bild geladen ist, um den genauen Wert für das boundingRectangle zu erhalten. Scheint, dass Firefox dieses "Problem" nicht hat
Window.innerHeight und innerWidth sind im IE undefiniert. Es gibt einen guten Artikel unter howtocreate.de/tutorials/javascript/browserwindow zeigt die verschiedenen Möglichkeiten, die Dokumenthöhe zu erhalten... document.body.clientHeight scheint der beste Weg insgesamt zu sein. Ich habe Ihre Antwort entsprechend bearbeitet.
Hier ist meine Version der Prüfung, sie prüft nur, ob das Element in vp überhaupt sichtbar ist: gist.github.com/2300296
Aktualisierung: Die Zeit schreitet voran, und das gilt auch für unsere Browser. Diese Technik wird nicht mehr empfohlen und Sie sollten Dans Lösung wenn Sie keine Version von Internet Explorer vor 7 unterstützen müssen.
Ursprüngliche Lösung (jetzt veraltet):
Damit wird geprüft, ob das Element im aktuellen Ansichtsfenster vollständig sichtbar ist:
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)
);
}
Sie können dies einfach ändern, um festzustellen, ob ein Teil des Elements im Ansichtsfenster sichtbar ist:
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
);
}
Die ursprünglich gepostete Funktion hatte einen Fehler. Musste die Breite/Höhe vor der Neuzuweisung von El...
Es könnte auch ratsam sein, dies ein wenig zu abstrahieren und einige Hilfsfunktionen zu erstellen. Ich verwende eine Funktion namens getViewport, die die obere/linke/untere/rechte Seite des sichtbaren Fensters zurückgibt, und eine Funktion namens getPosition, die die obere/linke Seite eines Elements ermittelt.
Ich versuche, diese Funktionalität in einer Firefox-Erweiterung zu erhalten, also nur für den Fall, dass jemand anderes in der gleichen Situation ist... (Web-Entwickler, können Sie natürlich diesen Kommentar vollständig ignorieren) Diese Funktion funktioniert perfekt in diesem Kontext, AUSSER, dass window
verweist auf das falsche Objekt, also setzen Sie einfach var window = el.ownerDocument.defaultView
am Anfang der Funktion, und schon kann es losgehen.
In modernen Browsern sollten Sie sich die Schnittpunkt Beobachter API die die folgenden Vorteile bietet:
Intersection Observer ist auf dem Weg zu einem vollwertigen Standard und wird bereits von Chrome 51+, Edge 15+ und Firefox 55+ unterstützt und ist für Safari in Entwicklung. Es gibt auch eine Polyfill verfügbar.
Es gibt einige Probleme mit dem Antwort von Dan die ihn für manche Situationen ungeeignet machen könnten. Einige dieser Probleme werden in seiner Antwort in der Nähe des Bodens darauf hingewiesen, dass sein Code falsch-positive Ergebnisse für Elemente liefert, die sind:
clip
EigenschaftDiese Einschränkungen werden in den folgenden Ergebnissen einer einfacher Test :
isElementVisible()
Hier ist eine Lösung für diese Probleme, mit dem Testergebnis unten und einer Erklärung einiger Teile des Codes.
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))
);
}
Bestehen der Prüfung: http://jsfiddle.net/AndyE/cAY8c/
Und das Ergebnis:
Diese Methode hat jedoch auch ihre Grenzen. So würde beispielsweise ein Element, das mit einem niedrigeren z-Index als ein anderes Element an derselben Stelle getestet wird, als ausgeblendet identifiziert werden, auch wenn das davor liegende Element tatsächlich keinen Teil davon verdeckt. Dennoch hat diese Methode in einigen Fällen ihre Berechtigung, die Dans Lösung nicht abdeckt.
Beide element.getBoundingClientRect()
y document.elementFromPoint()
sind Teil der CSSOM Working Draft-Spezifikation und werden mindestens von IE 6 und höher unterstützt. Die meisten Desktop-Browser für eine lange Zeit (wenn auch nicht perfekt). Siehe Quirksmode bei diesen Funktionen für weitere Informationen.
contains()
wird verwendet, um zu sehen, ob das Element, das von document.elementFromPoint()
ist ein untergeordneter Knoten des Elements, das wir auf Sichtbarkeit testen. Es gibt auch true zurück, wenn das zurückgegebene Element dasselbe Element ist. Dies macht die Prüfung einfach robuster. Sie wird von allen wichtigen Browsern unterstützt, wobei Firefox 9.0 der letzte von ihnen ist, der sie hinzufügt. Für ältere Firefox-Unterstützung, überprüfen Sie die Geschichte dieser Antwort.
Wenn Sie mehr Punkte um das Element herum auf Sichtbarkeit prüfen wollen, um sicherzustellen, dass das Element nicht zu mehr als, sagen wir, 50 % abgedeckt wird, wäre es nicht viel Aufwand, den letzten Teil der Antwort anzupassen. Beachten Sie jedoch, dass es wahrscheinlich sehr langsam wäre, wenn Sie jedes Pixel überprüfen würden, um sicherzustellen, dass es zu 100 % sichtbar ist.
Wollten Sie doc.documentElement.clientWidth verwenden? Sollte das stattdessen "document.documentElement" sein? Übrigens ist dies die einzige Methode, die auch für Anwendungsfälle wie das Ausblenden des Inhalts eines Elements für die Barrierefreiheit mit der CSS-Eigenschaft "clip" funktioniert: snook.ca/archives/html_and_css/hiding-content-for-accessibility
@klamping: Gut erkannt, danke. Ich hatte es direkt aus meinem Code kopiert, wo ich mit doc
als Alias für document
. Ja, ich halte das für eine vernünftige Lösung für die Grenzfälle.
Wir haben jetzt ein natives javascript Schnittpunkt Beobachter API anhand derer wir erkennen können, ob sich Elemente im Ansichtsfenster befinden oder nicht.
Hier ein Beispiel
const el = document.querySelector('#el')
const observer = new window.IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
console.log('ENTER')
return
}
console.log('LEAVE')
}, {
root: null,
threshold: 0.1, // set offset 0.1 means trigger if atleast 10% of element in viewport
})
observer.observe(el);
body {
height: 300vh;
}
#el {
margin-top: 100vh;
}
<div id="el">this is element</div>
Die beste Lösung, die ich je benutzt habe, für diejenigen, die noch daran interessiert sind, hier ist es eingerichtet, um die Klasse zu entfernen const observer = new window.IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { entry.target.classList.add("viewport__active"); return; } entry.target.classList.remove("viewport__active"); }, { root: null, threshold: 0.4 // 0.0 - 1.0 } );
Ich habe versucht Dans Antwort Die zur Bestimmung der Grenzen verwendete Algebra bedeutet jedoch, dass das Element sowohl die Größe des Ansichtsfensters haben als auch vollständig innerhalb des Ansichtsfensters liegen muss, um true
was leicht zu falsch negativen Ergebnissen führen kann. Wenn Sie feststellen wollen, ob sich ein Element überhaupt im Ansichtsfenster befindet, ryanve's Antwort ist nahe dran, aber das getestete Element sollte das Ansichtsfenster überlappen, also versuchen Sie dies:
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() */;
}
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
1 Stimmen
Es kommt darauf an, was Sie unter sichtbar verstehen. Wenn Sie meinen, dass es derzeit auf der Seite angezeigt wird, können Sie es anhand des y-Offsets des Elements und der aktuellen Bildlaufposition berechnen.
1 Stimmen
Ich habe hinzugefügt meine eigene Lösung die dieses Problem löst
1 Stimmen
Berücksichtigt eine dieser Lösungen den Z-Index eines Dom-Knotens und wie das die Sichtbarkeit speziell durch möglicherweise Ausblenden von Elementen mit einem niedrigeren Z-Index beeinflussen könnte?
1 Stimmen
Keine der angebotenen Antworten funktioniert mit absolut positionierten Elementen.
1 Stimmen
Eine Antwort für 2019: IntersectionObserver
3 Stimmen
Es gibt eine Million Antworten und die meisten sind lächerlich lang. Siehe hier für einen Zweizeiler
0 Stimmen
Verwenden Sie die Schnittpunkt-Beobachter-API, um zu erkennen, ob sich ein Element im Ansichtsfenster befindet, Details hier - frontendguruji.com/blog/
0 Stimmen
Zusätzlich zu den Antworten auf die Fragen von Intersection Observer eignet sich dieser Visualizer hervorragend für die Konfiguration der Variablen für Intersection Observer: codepen.io/michellebarker/pen/xxwLpRG