是否有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?
(这个问题指的是Firefox。)
是否有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?
(这个问题指的是Firefox。)
当前回答
在我看来,非常简单:
function isVisible(elem) {
var coords = elem.getBoundingClientRect();
return Math.abs(coords.top) <= coords.height;
}
其他回答
查看verge的源代码,它使用getBoundingClientRect。这就像:
function inViewport (element) {
if (!element) return false;
if (1 !== element.nodeType) return false;
var html = document.documentElement;
var rect = element.getBoundingClientRect();
return !!rect &&
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.left <= html.clientWidth &&
rect.top <= html.clientHeight;
}
如果元素的任何部分在视口中,则返回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;
}
在之前的回答中,大多数用法都没有做到这一点:
-当一个元素的任何像素都是可见的,但不是“一个角落”, -当一个元素大于viewport并且居中时, -大多数只检查文档或窗口内的单个元素。
好吧,对于所有这些问题,我都有一个解决方案,好的方面是:
-你可以返回可见时,只有一个像素从任何一方显示,而不是一个角落, -你仍然可以返回大于viewport的visible while元素, -你可以选择你的父元素,或者你可以自动让它选择。 -也适用于动态添加的元素
如果你检查下面的片段,你会发现在元素的容器中使用溢出滚动的区别不会造成任何麻烦,并且看到与其他答案不同的是,即使一个像素从任何一侧显示,或者当一个元素大于视口时,我们看到的是元素的内部像素,它仍然有效。
用法很简单:
// 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
一个没有crossSearchAlgorithm的演示,对于大于viewport的元素,检查element3内部像素,可以看到:
function isVisible(element, parent, crossSearchAlgorithm) { var rect = element.getBoundingClientRect(), prect = (parent != undefined) ? parent.getBoundingClientRect() : element.parentNode.getBoundingClientRect(), csa = (crossSearchAlgorithm != undefined) ? crossSearchAlgorithm : false, efp = function (x, y) { return document.elementFromPoint(x, y) }; // Return false if it's not in the viewport if (rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom) { return false; } var flag = false; // Return true if left to right any border pixel reached for (var x = rect.left; x < rect.right; x++) { if (element.contains(efp(rect.top, x)) || element.contains(efp(rect.bottom, x))) { flag = true; break; } } // Return true if top to bottom any border pixel reached if (flag == false) { for (var y = rect.top; y < rect.bottom; y++) { if (element.contains(efp(rect.left, y)) || element.contains(efp(rect.right, y))) { flag = true; break; } } } if(csa) { // Another algorithm to check if element is centered and bigger than viewport if (flag == false) { var x = rect.left; var y = rect.top; // From top left to bottom right while(x < rect.right || y < rect.bottom) { if (element.contains(efp(x,y))) { flag = true; break; } if(x < rect.right) { x++; } if(y < rect.bottom) { y++; } } if (flag == false) { x = rect.right; y = rect.top; // From top right to bottom left while(x > rect.left || y < rect.bottom) { if (element.contains(efp(x,y))) { flag = true; break; } if(x > rect.left) { x--; } if(y < rect.bottom) { y++; } } } } } return flag; } // Check multiple elements visibility document.getElementById('container').addEventListener("scroll", function() { var elementList = document.getElementsByClassName("element"); var console = document.getElementById('console'); for (var i=0; i < elementList.length; i++) { // I did not define parent, so it will be element's parent if (isVisible(elementList[i])) { console.innerHTML = "Element with id[" + elementList[i].id + "] is visible!"; break; } else { console.innerHTML = "Element with id[" + elementList[i].id + "] is hidden!"; } } }); // Dynamically added elements for(var i=4; i <= 6; i++) { var newElement = document.createElement("div"); newElement.id = "element" + i; newElement.classList.add("element"); document.getElementById('container').appendChild(newElement); } #console { background-color: yellow; } #container { width: 300px; height: 100px; background-color: lightblue; overflow-y: auto; padding-top: 150px; margin: 45px; } .element { margin: 400px; width: 400px; height: 320px; background-color: green; } #element3 { position: relative; margin: 40px; width: 720px; height: 520px; background-color: green; } #element3::before { content: ""; position: absolute; top: -10px; left: -10px; margin: 0px; width: 740px; height: 540px; border: 5px dotted green; background: transparent; } <div id="console"></div> <div id="container"> <div id="element1" class="element"></div> <div id="element2" class="element"></div> <div id="element3" class="element"></div> </div>
你看,当你在element3内部时,它无法判断它是否可见,因为我们只检查元素是否从侧面或角落可见。
这个包含了crossSearchAlgorithm,它允许你在元素大于viewport时仍然返回visible:
function isVisible(element, parent, crossSearchAlgorithm) { var rect = element.getBoundingClientRect(), prect = (parent != undefined) ? parent.getBoundingClientRect() : element.parentNode.getBoundingClientRect(), csa = (crossSearchAlgorithm != undefined) ? crossSearchAlgorithm : false, efp = function (x, y) { return document.elementFromPoint(x, y) }; // Return false if it's not in the viewport if (rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom) { return false; } var flag = false; // Return true if left to right any border pixel reached for (var x = rect.left; x < rect.right; x++) { if (element.contains(efp(rect.top, x)) || element.contains(efp(rect.bottom, x))) { flag = true; break; } } // Return true if top to bottom any border pixel reached if (flag == false) { for (var y = rect.top; y < rect.bottom; y++) { if (element.contains(efp(rect.left, y)) || element.contains(efp(rect.right, y))) { flag = true; break; } } } if(csa) { // Another algorithm to check if element is centered and bigger than viewport if (flag == false) { var x = rect.left; var y = rect.top; // From top left to bottom right while(x < rect.right || y < rect.bottom) { if (element.contains(efp(x,y))) { flag = true; break; } if(x < rect.right) { x++; } if(y < rect.bottom) { y++; } } if (flag == false) { x = rect.right; y = rect.top; // From top right to bottom left while(x > rect.left || y < rect.bottom) { if (element.contains(efp(x,y))) { flag = true; break; } if(x > rect.left) { x--; } if(y < rect.bottom) { y++; } } } } } return flag; } // Check multiple elements visibility document.getElementById('container').addEventListener("scroll", function() { var elementList = document.getElementsByClassName("element"); var console = document.getElementById('console'); for (var i=0; i < elementList.length; i++) { // I did not define parent so it will be element's parent // and it will do crossSearchAlgorithm if (isVisible(elementList[i],null,true)) { console.innerHTML = "Element with id[" + elementList[i].id + "] is visible!"; break; } else { console.innerHTML = "Element with id[" + elementList[i].id + "] is hidden!"; } } }); // Dynamically added elements for(var i=4; i <= 6; i++) { var newElement = document.createElement("div"); newElement.id = "element" + i; newElement.classList.add("element"); document.getElementById('container').appendChild(newElement); } #console { background-color: yellow; } #container { width: 300px; height: 100px; background-color: lightblue; overflow-y: auto; padding-top: 150px; margin: 45px; } .element { margin: 400px; width: 400px; height: 320px; background-color: green; } #element3 { position: relative; margin: 40px; width: 720px; height: 520px; background-color: green; } #element3::before { content: ""; position: absolute; top: -10px; left: -10px; margin: 0px; width: 740px; height: 540px; border: 5px dotted green; background: transparent; } <div id="console"></div> <div id="container"> <div id="element1" class="element"></div> <div id="element2" class="element"></div> <div id="element3" class="element"></div> </div>
JSFiddle玩:http://jsfiddle.net/BerkerYuceer/grk5az2c/
这段代码是为了获得更精确的信息,无论元素的任何部分是否显示在视图中。对于性能选项或仅垂直幻灯片,不要使用此选项!这段代码在绘图情况下更有效。
下面是检查给定元素在其父元素中是否完全可见的代码片段:
export const visibleInParentViewport = (el) => {
const elementRect = el.getBoundingClientRect();
const parentRect = el.parentNode.getBoundingClientRect();
return (
elementRect.top >= parentRect.top &&
elementRect.right >= parentRect.left &&
elementRect.top + elementRect.height <= parentRect.bottom &&
elementRect.left + elementRect.width <= parentRect.right
);
}
作为Element.getBoundingClientRect()的支持,最简单的解决方案已经变得完美:
function isInView(el) {
const box = el.getBoundingClientRect();
return box.top < window.innerHeight && box.bottom >= 0;
}