在CSS3列中获取可见文本

时间:2015-04-17 11:20:02

标签: javascript css3

尝试创建一个可以读取元素内部当前可见文本的方法。你看到的方法就像我过去几天一样。

除了使用插入符/范围之外,是否有更可靠的方法来获取元素中的可见文本?导致我遇到的问题是我有很多溢出的文本然后也被选中因为插入符号没有捕获textNode而是捕获父容器。

我的网页看起来像&的例子。为什么我遇到当前方法的问题:

enter image description here

  • 截至目前,盖尔拥有性能最友好的解决方案&最容易实施。

不确定我在这里有意义,否则请告诉我:)。


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;
}

2 个答案:

答案 0 :(得分:1)

在全球范围内,您必须测试每个字母以确定它是否可见。 由于块容器可以是部分可见的,并且知道其内容的哪些部分是可见的意味着分别测试它们,最多为字母粒度。
然而,不是测试每个字母,而是可以测试一组字母是否可见,并使用binary search method,减少它们,就像必须知道所有包含的字母是否可见一样。< / p>

第一种方法

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);

对于仅包含10000个字母的文本,处理持续时间约为10秒。

范围界面

使用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方法,我们可以测试是否存在与窗口边界的交集:

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

一些警告

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

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

答案 1 :(得分:0)

正如我在这里猜测的那样,您正在尝试解析DOM并获取所有元素文本 如果你看下面的内容: jQuery.parseHTML()

并查看DOM元素属性 DOM Element

查看示例以检索文本值 DOM element tetxt value

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

希望这可能会有所帮助