Javascript 获取CSS3列中的可见文本

Javascript 获取CSS3列中的可见文本,javascript,css,Javascript,Css,尝试创建一个可以读取元素内部当前可见文本的方法。你在下面看到的方法是我在过去几天里得到的 除了使用插入符号/范围之外,还有什么更可靠的方法来获取元素中的可见文本吗?因为我遇到的问题是,我有很多溢出的文本,这些文本也会被选中,因为插入符号不会捕获textNode,而是父容器 我的页面外观示例&为什么我对当前方法有问题: 到目前为止,Gael拥有最易于实现的性能友好型解决方案 不确定我在这里是否有意义,否则请让我知道:) function getTextInColumn (rect) {

尝试创建一个可以读取元素内部当前可见文本的方法。你在下面看到的方法是我在过去几天里得到的

除了使用插入符号/范围之外,还有什么更可靠的方法来获取元素中的可见文本吗?因为我遇到的问题是,我有很多溢出的文本,这些文本也会被选中,因为插入符号不会捕获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;
}
函数列(rect){
var startX=矩形左;
var startY=rect.top;
var endX=rect.left+rect.width-2;
var endY=rect.top+rect.height-2;
变量开始、结束、范围=空;
var i=0;
var rangeText='';
而((rangeText==''&&i<100&&endY>5)){
范围=空;
if(typeof document.caretPositionFromPoint!=“未定义”){
开始=document.caretPositionFromPoint(startX,startY);
结束=document.caretPositionFromPoint(endX,endY);
如果(开始!==null和结束!==null){
range=document.createRange();
range.setStart(start.offsetNode,start.offset);
range.setEnd(end.offsetNode,end.offset);
}
}
else if(typeof document.caretRangeFromPoint!=“未定义”){
开始=document.caretRangeFromPoint(startX,startY);
end=document.caretRangeFromPoint(endX,endY);
如果(开始!==null和结束!==null){
range=document.createRange();
range.setStart(start.startContainer,start.startOffset);
range.setEnd(end.startContainer,end.startOffset);
}
}
如果(范围!==null){
rangeText=range.toString();
}
endY-=52;
i++;
}
返回文本;
}

正如我猜的,您正在尝试解析DOM并获取所有文本元素 如果您查看以下内容:

然后看看DOM元素属性

查看示例以检索文本值

如果你发布你的html代码,你会得到更好的帮助


希望这对全球有所帮助,你必须测试每个字母是否可见。 由于块容器可以部分可见,并且知道其内容的哪些部分是可见的,这意味着要对它们进行单独测试,直到达到字母粒度。
然而,不必测试每个字母,而是可以测试一组字母是否可见,并使用a减少它们,就像知道是否所有包含的字母都可见一样

第一种方法

textNode的位置、维度(例如边界矩形)不在其属性中

因此,我最初尝试在块元素中插入textNodes,但这会导致性能问题,因为修改DOM意味着回流

此外,我们不仅要确定文本节点是否可见,还要确定其所有字母是否可见。所以我把每个字母放在一个块元素中:

    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);
然后,由于
range
具有与块容器相同的方法,我们可以测试是否与窗口边界相交:

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 ];
    }
  }
对于相同的文本,处理持续时间为数十毫秒。 而且复杂性并不随文本长度的增加而增加

完整的代码是

一些注意事项

它只获取一个textNode的可见文本,但同样的二进制搜索逻辑也可以应用于DOM树


所有可见范围在过程结束时合并;这可能是一个问题,因为相邻行不能连接

我不太清楚:你想抓取页面可见部分中的所有文本吗?@Gael-我不想抓取DOM元素中的所有可见文本,DOM元素显示在我附加到帖子的图像中。这里需要注意的是,所有的文本都在同一个元素中。你能给我们相关的html吗?你能检查一下这是否是你想要实现的:@Gael-这正是我想要的。这种方法唯一关心的是性能。当我使用这个解决方案索引200多页时。我将对实现进行一次尝试,并将结果反馈给您:)非常感谢您给出如此详细的示例/方法。但正如示例所示。。对我来说,这是一个有趣的话题;)对其进行了调整,使其能够与HTML内容一起工作&这样开发人员就可以将测试矩形发送给该方法。-这至少是我最后使用的:)您的解决方案比我以前的解决方案好得多。这要快得多我喜欢树行者:)
 /*
     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 ];
    }
  }