获取CSS3列中的可见文本 [英] Get visible text in CSS3 columns

查看:30
本文介绍了获取CSS3列中的可见文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试创建一种方法,该方法可以读取元素内部的当前可见文本.到目前为止,您在下面看到的方法是我过去几天所掌握的.

Trying to make a method which can read the current visible text inside of a element. The method you see below is as far as i've gotten the past few days.

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

Is there anything more reliable for getting the visible text in a element other than using a caret/range? Cause the issue I'm having is that i have a lot of overflown text who then also gets selected because caret doesn't catch the textNode but instead the parent container.

我的页面外观为&的示例为什么我对当前方法有疑问:

An example of what my page looks like & why I'm having issues with current method:

  • 截至目前,Gael拥有最性能友好的解决方案&最容易实现.

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

Not sure I'm making sense here, otherwise let me know :)


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

推荐答案

在全球范围内,您必须测试每个字母才能知道它是否可见.因为一个块状容器可以部分可见,所以知道其内容的哪些部分是可见的就意味着要分别测试它们,直到字母粒度为止.
但是,代替测试每个字母,可以测试一组字母是否可见,并使用二进制搜索方法,只需减少它们就可以知道所包含的所有字母是否可见.

Globally, you have to test each letter to know if it is visible. As a block container can be partially visible, and knowing which parts of its contents is visible implies to test them separatly, up to a letter granularity.
Yet, instead of testing each letter, it is possible to test if a set of letters is visible, and using a binary search method, reduce them just as that is necessary to know whether or not all the contained letters are visible.

第一种方法

textNode的位置,尺寸(例如边界矩形)不在其属性中.

The position, the dimension (eg. the bounding rect) of a textNode is not in its properties.

因此,我最初尝试将textNodes插入到block元素中,但这会导致性能问题,因为修改DOM会导致重排.

So, I initially tried to insert textNodes in a block element, but that was causing performance issues, as modifying the DOM implies reflows.

此外,我们不仅必须确定文本节点是否可见,还必须确定其所有字母.因此,我将每个字母放在一个块元素中:

More over, we have to not only determine if a text node is visible, but for all of its letters. So I put each letter in a block element:

    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个字母的文本,处理时间约为10s.

For a text which contains only 10000 letters, the processing duration was about 10s.

范围界面

使用范围来获取一组字母似乎是一种更好的方法,因为我们不必闯入DOM树.

Using ranges to get the position of a set of letters seems a better approach, as we do not have to break into the DOM tree.

最初将范围定义为包含所有文本:

A range is initially defined to contains all the text:

  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 具有相同的

then, as range has the same getBoundingClientRect methods as a block container, we can test if there is an intersection with the window bounds:

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

这给出了范围的显示方式:完全,部分显示,完全不显示:

This gives how a range is visible: completly, partially, on none:

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

如果一个范围是部分可见的,则将其分为两个子集,然后将对其进行测试,直到获得一个可见或不可见但不是部分可见的子集.

If a range is partially visible, it is splitted in two subsets, which will be then tested, up to obtain subsets which are visible or not, but not partially.

 /*
     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 ];
    }
  }

使用相同的文本,处理持续时间约为数十毫秒.而且复杂度不会随文本长度的增加而特别增加.

With the same text, the processing duration is in the order of tens ms. And the complexity does not particularly grow with the text length.

完整代码位于此处

一些警告

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

It just get visible text of one textNode, but the same logic of binary search could be applied to a DOM tree.

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

All visible ranges are merged at the end of the process; that could be an issue, as not adjacent lines could be concatenated.

这篇关于获取CSS3列中的可见文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆