javascript - 获取 CSS3 列中的可见文本

标签 javascript css

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

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

我的页面外观示例以及当前方法出现问题的原因:

enter image description here

  • 截至目前,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/

相关文章:

javascript - JQuery .hover/.on ('mouseleave' ) 无法正常运行

css - 在 Bootstrap 3 中将 Glyphicons 放在导航栏元素的前面?

html - 使 Angular Material 数据表行滚动,同时保持标题的粘性

javascript - 最后一段 javascript 闭包我还是不明白

javascript - 如何计算对象数组中的数字

javascript - Rails 每个循环中的 JQuery 错误(未捕获的 ReferenceError : $ is not defined)

javascript - CSS JavaScript 链接

带有可选捕获组的 JavaScript 正则表达式 - 匹配 mongo 查询

html - Flexbox:按比例增加或减少中间元素的高度

html - 是否可以在多个表格行上设置一个线性渐变背景?