StackOverflow上有很多关于如何检查元素是否真正可见于视口的问题,但它们都寻求一个布尔型的答案。我想知道的是元素实际可见的区域。
注:loaded with questions需要根据具体语境翻译,无法确定具体含义。
function getVisibleAreas(e) {
...
return rectangleSet;
}
更正式地说,元素的“可见区域”是CSS坐标系中的一组(最好不重叠的)矩形,其中如果点(x,y)包含在集合的至少一个矩形中,则
elementFromPoint(x, y)
将返回该元素。调用此函数对所有DOM元素(包括iframe)的结果应该是一组非重叠区域集合,其并集是整个视口区域。
我的目标是创建某种视口“转储”数据结构,它可以有效地返回给定点在视口中的单个元素,反之亦然-对于转储中的给定元素,它将返回可见区域的集合。 (当我需要查询视口结构时,将向远程客户端应用程序传递数据结构,因此我不一定能访问实际文档)。
实现要求:
hidden
状态、z-index
、头和脚等。
当然,我可能有点天真,会对视口中的每个离散点调用 elementFromPoint
,但性能非常关键,因为我要迭代所有元素,并且经常这样做。
请指导我如何实现这个目标。
免责声明:我对 web 编程概念相当陌生,所以可能使用了错误的技术术语。
进展:
我想出了一个实现方法。算法非常简单:
- 遍历所有元素,并将它们的垂直/水平线添加到坐标图中(如果坐标在视口内)。
- 对于步骤1中生成的每个“矩形”的中心位置调用 `document.elementFromPoint`。矩形是从步骤1中的地图中两个相邻垂直坐标和两个相邻水平坐标之间的区域。
这将产生一组区域/矩形,每个指向一个单独的元素。
我的实现存在以下问题:- 对于复杂的页面效率低下(对于大屏幕和Gmail收件箱可能需要2-4分钟)。
- 每个元素产生了大量矩形,使得将其转换为字符串并通过网络发送变得低效,并且使用起来不方便(我希望每个元素最终只有尽可能少的矩形集合)。
elementFromPoint
调用占用了大量时间,导致我的算法相对无用...有人能提出更好的方法吗?
以下是我的实现:
function AreaPortion(l, t, r, b, currentDoc) {
if (!currentDoc) currentDoc = document;
this._x = l;
this._y = t;
this._r = r;
this._b = b;
this._w = r - l;
this._h = b - t;
center = this.getCenter();
this._elem = currentDoc.elementFromPoint(center[0], center[1]);
}
AreaPortion.prototype = {
getName: function() {
return "[x:" + this._x + ",y:" + this._y + ",w:" + this._w + ",h:" + this._h + "]";
},
getCenter: function() {
return [this._x + (this._w / 2), this._y + (this._h / 2)];
}
}
function getViewport() {
var viewPortWidth;
var viewPortHeight;
// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
if (
typeof document.documentElement != 'undefined' &&
typeof document.documentElement.clientWidth != 'undefined' &&
document.documentElement.clientWidth != 0) {
viewPortWidth = document.documentElement.clientWidth,
viewPortHeight = document.documentElement.clientHeight
}
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
else if (typeof window.innerWidth != 'undefined') {
viewPortWidth = window.innerWidth,
viewPortHeight = window.innerHeight
}
// older versions of IE
else {
viewPortWidth = document.getElementsByTagName('body')[0].clientWidth,
viewPortHeight = document.getElementsByTagName('body')[0].clientHeight
}
return [viewPortWidth, viewPortHeight];
}
function getLines() {
var onScreen = [];
var viewPort = getViewport();
// TODO: header & footer
var all = document.getElementsByTagName("*");
var vert = {};
var horz = {};
vert["0"] = 0;
vert["" + viewPort[1]] = viewPort[1];
horz["0"] = 0;
horz["" + viewPort[0]] = viewPort[0];
for (i = 0 ; i < all.length ; i++) {
var e = all[i];
// TODO: Get all client rectangles
var rect = e.getBoundingClientRect();
if (rect.width < 1 && rect.height < 1) continue;
var left = Math.floor(rect.left);
var top = Math.floor(rect.top);
var right = Math.floor(rect.right);
var bottom = Math.floor(rect.bottom);
if (top > 0 && top < viewPort[1]) {
vert["" + top] = top;
}
if (bottom > 0 && bottom < viewPort[1]) {
vert["" + bottom] = bottom;
}
if (right > 0 && right < viewPort[0]) {
horz["" + right] = right;
}
if (left > 0 && left < viewPort[0]) {
horz["" + left] = left;
}
}
hCoords = [];
vCoords = [];
//TODO:
for (var v in vert) {
vCoords.push(vert[v]);
}
for (var h in horz) {
hCoords.push(horz[h]);
}
return [hCoords, vCoords];
}
function getAreaPortions() {
var portions = {}
var lines = getLines();
var hCoords = lines[0];
var vCoords = lines[1];
for (i = 1 ; i < hCoords.length ; i++) {
for (j = 1 ; j < vCoords.length ; j++) {
var portion = new AreaPortion(hCoords[i - 1], vCoords[j - 1], hCoords[i], vCoords[j]);
portions[portion.getName()] = portion;
}
}
return portions;
}
offset
的概念有了一些想法,但实际上,它并不符合我的要求... - ElistelementFromPoint
的JavaScript实现?这对我来说将是一个很好的起点。 - Elist