是否有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?
(这个问题指的是Firefox。)
是否有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?
(这个问题指的是Firefox。)
当前回答
我使用这个函数(它只检查y是否在屏幕上,因为大多数时候x是不需要的)
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;
}
}
其他回答
作为Element.getBoundingClientRect()的支持,最简单的解决方案已经变得完美:
function isInView(el) {
const box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}
我尝试了Dan的答案,然而,用于确定边界的代数意味着元素必须既≤视口大小,又完全在视口内才能为真,很容易导致假否定。如果你想确定一个元素是否在视口中,ryanve的答案是接近的,但被测试的元素应该与视口重叠,所以试试这个:
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() */;
}
/**
* Returns Element placement information in Viewport
* @link https://stackoverflow.com/a/70476497/2453148
*
* @typedef {object} ViewportInfo - Whether the element is…
* @property {boolean} isInViewport - fully or partially in the viewport
* @property {boolean} isPartiallyInViewport - partially in the viewport
* @property {boolean} isInsideViewport - fully inside viewport
* @property {boolean} isAroundViewport - completely covers the viewport
* @property {boolean} isOnEdge - intersects the edge of viewport
* @property {boolean} isOnTopEdge - intersects the top edge
* @property {boolean} isOnRightEdge - intersects the right edge
* @property {boolean} isOnBottomEdge - is intersects the bottom edge
* @property {boolean} isOnLeftEdge - is intersects the left edge
*
* @param el Element
* @return {Object} ViewportInfo
*/
function getElementViewportInfo(el) {
let result = {};
let rect = el.getBoundingClientRect();
let windowHeight = window.innerHeight || document.documentElement.clientHeight;
let windowWidth = window.innerWidth || document.documentElement.clientWidth;
let insideX = rect.left >= 0 && rect.left + rect.width <= windowWidth;
let insideY = rect.top >= 0 && rect.top + rect.height <= windowHeight;
result.isInsideViewport = insideX && insideY;
let aroundX = rect.left < 0 && rect.left + rect.width > windowWidth;
let aroundY = rect.top < 0 && rect.top + rect.height > windowHeight;
result.isAroundViewport = aroundX && aroundY;
let onTop = rect.top < 0 && rect.top + rect.height > 0;
let onRight = rect.left < windowWidth && rect.left + rect.width > windowWidth;
let onLeft = rect.left < 0 && rect.left + rect.width > 0;
let onBottom = rect.top < windowHeight && rect.top + rect.height > windowHeight;
let onY = insideY || aroundY || onTop || onBottom;
let onX = insideX || aroundX || onLeft || onRight;
result.isOnTopEdge = onTop && onX;
result.isOnRightEdge = onRight && onY;
result.isOnBottomEdge = onBottom && onX;
result.isOnLeftEdge = onLeft && onY;
result.isOnEdge = result.isOnLeftEdge || result.isOnRightEdge ||
result.isOnTopEdge || result.isOnBottomEdge;
let isInX =
insideX || aroundX || result.isOnLeftEdge || result.isOnRightEdge;
let isInY =
insideY || aroundY || result.isOnTopEdge || result.isOnBottomEdge;
result.isInViewport = isInX && isInY;
result.isPartiallyInViewport =
result.isInViewport && result.isOnEdge;
return result;
}
一个更好的解决方案:
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;
}
现在大多数浏览器都支持getBoundingClientRect方法,这已经成为最佳实践。使用旧的答案非常慢,不准确,有几个bug。
被选为正确的解几乎从来不是精确的。
该解决方案在Internet Explorer 7(及更高版本)、iOS 5(及更高版本)Safari、Android 2.0 (Eclair)及更高版本、黑莓、Opera Mobile和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() */
);
}
使用方法:
您可以确定上面给出的函数在被调用时返回正确的答案,但是跟踪元素作为事件的可见性呢?
将以下代码放在<body>标签的底部:
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);
}
*/
如果你做了任何DOM修改,它们当然会改变元素的可见性。
指导方针和常见陷阱:
也许你需要跟踪页面缩放/移动设备缩放?jQuery应该处理跨浏览器缩放/缩放,否则第一个或第二个链接应该帮助你。
如果修改DOM,它会影响元素的可见性。您应该控制它并手动调用handler()。不幸的是,我们没有任何跨浏览器的onrepaint事件。另一方面,这允许我们只对可能改变元素可见性的DOM修改进行优化和重新检查。
永远不要只在jQuery $(document).ready()中使用它,因为没有保证CSS已经应用在这个时刻。您的代码可以在硬盘驱动器上与CSS一起本地工作,但一旦放在远程服务器上,它就会失败。
在触发DOMContentLoaded之后,应用了样式,但是还没有加载图像。我们应该加上window。Onload事件监听器。
我们还不能捕捉缩放/缩放事件。
最后的方法可以是下面的代码:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
你可以使用HTML5 API的pageVisibiliy功能,如果你关心你的网页标签是否活跃和可见。
此方法不能处理以下两种情况:
使用z-index进行重叠。 在元素的容器中使用溢出滚动。 尝试一些新的东西-路口观察者API解释。