是否有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?
(这个问题指的是Firefox。)
是否有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?
(这个问题指的是Firefox。)
当前回答
一个更好的解决方案:
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;
}
其他回答
Domysee的答案https://stackoverflow.com/a/37998526接近正确。
许多示例使用“完全包含在视口中”,他的代码使用百分比来允许部分可见。他的代码还解决了“是否是父视图剪切”的问题,大多数示例都忽略了这个问题。
一个缺失的元素是父对象滚动条的影响——getBoundingClientRect返回父对象的外部矩形(包含滚动条),而不是内部矩形(不包含滚动条)。子滚动条可以隐藏在父滚动条后面,当它不可见时,它被认为是可见的。
推荐的观察者模式不适合我的用例:使用方向键更改表中当前选择的行,并确保新选择是可见的。使用观察器进行此操作将过于复杂。
这是一些代码
它包括一个额外的hack (fudgeY),因为我的表有一个粘头,不是通过直接的方式检测(自动处理这个将是相当乏味的)。此外,对于所需的可见分数,它使用十进制(0到1)而不是百分比。(对于我的例子,我需要完整的y, x是不相关的)。
function intersectRect(r1, r2) {
var r = {};
r.left = r1.left < r2.left ? r2.left : r1.left;
r.top = r1.top < r2.top ? r2.top : r1.top;
r.right = r1.right < r2.right ? r1.right : r2.right;
r.bottom = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;
if (r.left < r.right && r.top < r.bottom)
return r;
return null;
}
function innerRect(e) {
var b,r;
b = e.getBoundingClientRect();
r = {};
r.left = b.left;
r.top = b.top;
r.right = b.left + e.clientWidth;
r.bottom = b.top + e.clientHeight;
return r;
}
function isViewable(e, fracX, fracY, fudgeY) {
// ref https://stackoverflow.com/a/37998526
// intersect all the rects and then check the result once
// innerRect: mind the scroll bars
// fudgeY: handle "sticky" thead in parent table. Ugh.
var r, pr, er;
er = e.getBoundingClientRect();
r = er;
for (;;) {
e = e.parentElement;
if (!e)
break;
pr = innerRect(e);
if (fudgeY)
pr.top += fudgeY;
r = intersectRect(r, pr);
if (!r)
return false;
}
if (fracX && ((r.right-r.left) / (er.right-er.left)) < (fracX-0.001))
return false;
if (fracY && ((r.bottom-r.top) / (er.bottom-er.top)) < (fracY-0.001))
return false;
return true;
}
我使用这个函数(它只检查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;
}
}
一个更好的解决方案:
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;
}
在Android上放大谷歌Chrome浏览器时,最被接受的答案是不工作的。结合Dan的回答,要考虑Android上的Chrome,必须使用visualViewport。下面的例子只考虑了垂直检查,并使用jQuery来计算窗口高度:
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);
作为Element.getBoundingClientRect()的支持,最简单的解决方案已经变得完美:
function isInView(el) {
const box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}