尝试创建一个可以读取元素内当前可见文本的方法。你在下面看到的方法是我最近几天得到的。
除了使用插入符号/范围之外,还有什么方法可以更可靠地获取元素中的可见文本吗?因为我遇到的问题是我有很多溢出的文本,然后它们也被选中,因为插入符号没有捕获 textNode 而是父容器。
我的页面外观示例以及当前方法出现问题的原因:
- 截至目前,Gael 拥有性能最友好且最易于实现的解决方案。
不确定我在这里是否有意义,否则请告诉我:)
function getTextInColumn (rect) {
var startX = rect.left;
var startY = rect.top;
var endX = rect.left + rect.width - 2;
var endY = rect.top + rect.height - 2;
var start, end, range = null;
var i = 0;
var rangeText = '';
while ((rangeText === '' && i < 100 && endY > 5)) {
range = null;
if (typeof document.caretPositionFromPoint != 'undefined') {
start = document.caretPositionFromPoint(startX, startY);
end = document.caretPositionFromPoint(endX, endY);
if (start !== null && end !== null) {
range = document.createRange();
range.setStart(start.offsetNode, start.offset);
range.setEnd(end.offsetNode, end.offset);
}
}
else if (typeof document.caretRangeFromPoint != 'undefined') {
start = document.caretRangeFromPoint(startX, startY);
end = document.caretRangeFromPoint(endX, endY);
if (start !== null && end !== null) {
range = document.createRange();
range.setStart(start.startContainer, start.startOffset);
range.setEnd(end.startContainer, end.startOffset);
}
}
if (range !== null) {
rangeText = range.toString();
}
endY -= 52;
i++;
}
return rangeText;
}
最佳答案
在全局范围内,您必须测试每个字母才能知道它是否可见。
由于 block 容器可以是部分可见的,并且知道其内容的哪些部分是可见的意味着要分别测试它们,直到字母粒度。
然而,不是测试每个字母,而是可以测试一组字母是否可见,并使用 binary search method , 减少它们,就像了解所有包含的字母是否可见所必需的那样。
第一种方法
文本节点的位置、尺寸(例如边界矩形)不在其属性中。
因此,我最初尝试在 block 元素中插入 textNode,但这会导致性能问题,因为修改 DOM 意味着回流。
此外,我们不仅要确定一个文本节点是否可见,还要确定它的所有字母。所以我把每个字母放在一个 block 元素中:
var text= textNode.nodeValue;
var markedText= text.replace(/(.)/g,"<span class='marker'>$1</span>");
var markedContainer= document.createElement("div");
markedContainer.innerHTML= markedText;
textContainer.replaceChild(markedContainer,textNode);
对于仅包含 10000 个字母的文本,处理时间约为 10 秒。
范围接口(interface)
使用 ranges获取一组字母的位置似乎是更好的方法,因为我们不必进入 DOM 树。
范围最初定义为包含所有文本:
var textNode = textContainer.childNodes[0]; //assumes that there is only one child and that it is a textNode
var range = document.createRange();
range.selectNodeContents(textNode);
然后,因为 range
具有相同的 getBoundingClientRect方法作为 block 容器,我们可以测试是否与窗口边界有交集:
function intersectionArea(a, b) {
//credits to http://math.stackexchange.com/a/99576
var x_overlap = Math.max(0, Math.min(a.right, b.right)
- Math.max(a.left, b.left))
var y_overlap = Math.max(0, Math.min(a.bottom, b.bottom)
- Math.max(a.top, b.top));
return x_overlap * y_overlap;
}
这给出了一个范围是如何可见的:完全、部分、无:
var intersection = intersectionArea(a, b);
if (intersection == 0)
state= "NULL";
else {
//that means that a is completly in b
if (intersection == intersectionArea(a, a))
state= "COMPLETE";
else
state= "PARTIAL";
}
如果一个范围是部分可见的,则将其分成两个子集,然后对其进行测试,直到获得可见或不可见但不是部分可见的子集。
/*
ranges are indexed in an object by their startOffset property
*/
var ranges = { 0 : range };
/*
rangesIdx contains all the ranges which have to be tested
*/
var rangesIdx = [0];
while ( rangesIdx.length > 0 ) {
var range = ranges[ rangesIdx.shift() ];
switch (overlapsVisibleContent(range)){
case "PARTIAL":
// if a range is partially visible, it is splitted on its middle
// the two resulting ranges will then be tested
if (range.endOffset - range.startOffset > 1){
//even if one letter is not completly visible, it is considered to be completly.
var rangeLastPart= splitRange(range);
ranges[ rangeLastPart.startOffset ] = rangeLastPart;
rangesIdx.push( rangeLastPart.startOffset );
rangesIdx.push( range.startOffset );
}else
if( paintingMode )
paint( range.getBoundingClientRect(), "COMPLETE" )
break;
case "COMPLETE":
// if a range is completly visible, it stays on the ranges object
break;
case "NULL":
// if a range is completly unvisible, it is deleted from the ranges object.
delete ranges[ range.startOffset ];
}
}
同样的文本,处理时间在几十毫秒量级。 并且复杂性不会随着文本长度的增加而特别增加。
完整代码为here
一些注意事项
它只是获取一个文本节点的可见文本,但是二分查找的相同逻辑可以应用于 DOM 树。
所有可见范围在过程结束时合并;这可能是一个问题,因为相邻的行不能连接起来。
关于javascript - 获取 CSS3 列中的可见文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29697776/