在文本选择中获取元素 [英] Get elements in text selection

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

问题描述

我想获取用户选择中包含的所有元素(如在DOM 2范围/ MS TextRanges中)

  / ** @return {Array。< Element>} * / 
function getSelectedElements(){

var elements = [];

//在用户选择中获取元素

返回元素;

}

我尝试这样做, 解决类似问题,以及一些Moz和MS文档以及一些PPK内容。



方法基本上是:







  • 定义SelectionLikeObject作为DOM选择或IE选择。


  • 将RangeLikeObject定义为DOM范围或IE TextRange。


  • containerNode 成为一个节点。


  • containerElement 成为一个元素。


  • containsElements 成为一个NodeList。 / p>


  • elementRange 成为RangeLikeObject。


  • selectedRange 成为RangeLikeObject。


  • selectedElements 是一个元素数组。


  • 元素成为一个元素。 / p>


  • 选择成为SelectionLikeObject。


  • 设置选择


  • selectedElements 设置为新的数组。


  • 对于选择中的每个 selectedRange




    • containerNode 设置为 selectedRange的共同祖先容器


    • containerElement 设置到最接近的元素祖先 containerNode


    • containedElements 设置为 containerElement


    • 对于元素 > containsElements :




      • 设置 elementRange 元素


      • 如果 elementRange 落在 selectedRange
        边界内:




        • 元素 $ selectedElements









      DOM分支如下所示:

        / ** 
      @param {Document} doc
      @return {Array。< Element> }
      * /
      getSelectedElements.fromDOM = function(doc){

      / ** @type {Range} * /
      var selectedRange;

      / ** @type {Array。< Element>} * /
      var selectedElements = [];

      / ** @type {Node} * /
      var containerNode;

      / ** @type {Element} * /
      var containerElement;

      / ** @type {NodeList} * /
      var containedElements;

      / ** @type {Range} * /
      var elementRange;

      / ** @type {Element} * /
      var element;

      / ** @type {Selection} * /
      var selection = doc.defaultView.getSelection();

      / ** @type {number} * /
      var rangeCount = selection.rangeCount;

      / ** @type {number} * /
      var elementCount;

      / ** @type {number} * /
      var i;

      //没有getRangeAt的浏览器的黑客
      //参见http://www.quirksmode.org/dom/range_intro.html

      if(!selection。 getRangeAt){

      selection.getRangeAt = function(i){
      / ** @type {Range} * /
      var range = doc.createRange();
      if(i ||!selection.anchorNode){
      return range;
      }
      range.setStart(selection.anchorNode,selection.anchorOffset);
      range.setEnd(selection.focusNode,selection.focusOffset);
      返回范围;

      };

      selection.rangeCount = 1;

      }

      elementRange = doc.createRange(); (i = 0; i< rangeCount; ++ i)

      {

      selectedRange = selection.getRangeAt(i);

      containerNode = selectedRange.commonAncestorContainer;

      while(containerNode&& containerNode.nodeType!= 1){

      containerNode = containerNode.parentNode;

      }

      if(!containerNode){

      return selectedElements; //发生错误...

      }

      containerElement = / ** @type {Element} * / containerNode;

      containedElements = containerElement.getElementsByTagName('*');

      elementCount = containedElements.length; (var i = 0; i< elementCount; ++ i)

      {

      element = containedElements [i];

      elementRange.selectNodeContents(element);

      if(elementRange.compareBoundaryPoints(selectedRange.END_TO_START,selectedRange)< 1&&b $ b elementRange.compareBoundaryPoints(selectedRange.START_TO_END,selectedRange)> -1){

      selectedElements.push(element);

      }
      }
      }

      elementRange.detach();

      return selectedElements;

      };

      IE分支如下所示:

        / ** 
      @param {Document} doc
      @return {Array。< Element>}
      * /
      getSelectedElements.fromIE = function(doc){

      //选择 - http://msdn.microsoft.com/en-us/library/ie/dd347133(v=vs.85).aspx
      // TextRange - http://msdn.microsoft.com/en-us/library/dd347140(v=vs.85).aspx
      // ControlRange - http://msdn.microsoft.com/en- us / library / ie / ms537447(v = vs85).aspx

      / ** @type {TextRange | ControlRange} * /
      var ieRange = doc.selection&& doc.selection.createRange();

      / ** @type {Array。< Element>} * /
      var selectedElements = [];

      / ** @type {TextRange} * /
      var selectedRange;

      / ** @type {Element} * /
      var containerElement;

      / ** @type {NodeList} * /
      var containedElements;

      / ** @type {TextRange} * /
      var elementRange;

      / ** @type {Element} * /
      var element;

      / ** @type {Selection} * /
      var selection;

      / ** @type {number} * /
      var i = -1;


      if(ieRange.text === void 0){

      return []; // FIXME:这是一个ControlRange,放弃。

      }

      selectedRange = / ** @type {TextRange} * / ieRange;

      containerElement = selectedRange.parentElement();

      containedElements = containerElement.getElementsByTagName('*');

      elementRange = doc.body.createTextRange();

      while((element = containedElements [++ i])){

      elementRange.moveToElementText(element);

      if(elementRange.compareEndPoints(StartToEnd,selectedRange)> -1&&
      elementRange.compareEndPoints(EndToStart,selectedRange)< 1){

      selectedElements.push(element);

      }
      }

      return / ** @type {Array。< Element>} * / selectedElements;

      };

      现在,我想解决的问题是这样的:如果一个元素中只有部分文本是选择它,它出现在返回的数组中,即使它只是部分选择



      我想添加一个将行为更改为仅包括完全选择的参数元素。我有一种感觉,compareBoundaryPoints的答案就是这样,我只是不明白这一点,可以弄清楚。



      此外,IE代码迄今未经测试,但是如果有什么问题(或DOM分支),请让我知道。

      解决方案

      睡觉后阅读 compareBoundaryPoints ,我想我有答案。

        if(elementRange.compareBoundaryPoints(Range.START_TO_START,selectedRange)> -1&& 
      elementRange.compareBoundaryPoints(Range.END_TO_END,selectedRange)< 1){

      对于完全落在用户选择范围内的元素,这似乎只能评估 true p>

      I want to get all of the elements contained in a user selection (as in DOM 2 ranges / MS TextRanges).

      /** @return {Array.<Element>} */
      function getSelectedElements() {
      
        var elements = [];
      
        // get elements in the user selection somehow
      
        return elements;
      
      }
      

      I've tried to do this by following Tim Down's excellent solution to a similar question, and some Moz and MS docs, and some PPK stuff.

      The approach is basically:


      • Define SelectionLikeObject as a DOM Selection or an IE Selection.

      • Define RangeLikeObject as a DOM Range or an IE TextRange.

      • Let containerNode be a Node.

      • Let containerElement be an Element.

      • Let containedElements be a NodeList.

      • Let elementRange be a RangeLikeObject.

      • Let selectedRange be a RangeLikeObject.

      • Let selectedElements be an Array of Elements.

      • Let element be an Element.

      • Let selection be a SelectionLikeObject.

      • Set selection from the user's selection.

      • Set selectedElements to a new Array.

      • For each selectedRange in selection:

        • Set containerNode to the common ancestor container of selectedRange.

        • Set containerElement to the closest Element ancestor to containerNode.

        • Set containedElements to a list of descendants of containerElement.

        • For each element in containedElements:

          • Set elementRange from element.

          • If the boundaries of elementRange fall within the boundaries of selectedRange:

            • Push element onto selectedElements.

      The DOM branch looks like this:

      /** 
          @param {Document} doc
          @return {Array.<Element>} 
      */
      getSelectedElements.fromDOM = function (doc) {
      
        /** @type {Range} */
        var selectedRange;
      
        /** @type {Array.<Element>} */
        var selectedElements = [];
      
        /** @type {Node} */
        var containerNode;
      
        /** @type {Element} */
        var containerElement;
      
        /** @type {NodeList} */
        var containedElements;
      
        /** @type {Range} */
        var elementRange;
      
        /** @type {Element} */
        var element;
      
        /** @type {Selection} */
        var selection = doc.defaultView.getSelection();
      
        /** @type {number} */
        var rangeCount = selection.rangeCount;
      
        /** @type {number} */
        var elementCount;
      
        /** @type {number} */
        var i;
      
        // hack for browsers without getRangeAt
        // see http://www.quirksmode.org/dom/range_intro.html
      
        if (!selection.getRangeAt) {
      
          selection.getRangeAt = function (i) {
            /** @type {Range} */
            var range = doc.createRange();
            if (i || !selection.anchorNode) {
              return range;
            }
            range.setStart(selection.anchorNode, selection.anchorOffset);
            range.setEnd(selection.focusNode, selection.focusOffset);
            return range;
      
          };
      
          selection.rangeCount = 1;
      
        }
      
        elementRange = doc.createRange();
      
        for (i = 0; i < rangeCount; ++i) {
      
          selectedRange = selection.getRangeAt(i);
      
          containerNode = selectedRange.commonAncestorContainer;
      
          while (containerNode && containerNode.nodeType != 1) {
      
            containerNode = containerNode.parentNode;
      
          }
      
          if (!containerNode) {
      
            return selectedElements; // something went wrong...
      
          }
      
          containerElement = /** @type {Element} */ containerNode;
      
          containedElements = containerElement.getElementsByTagName('*');
      
          elementCount = containedElements.length;
      
          for (var i = 0; i < elementCount; ++i) {
      
            element = containedElements[i];
      
            elementRange.selectNodeContents(element);
      
            if (elementRange.compareBoundaryPoints(selectedRange.END_TO_START, selectedRange) < 1 &&
                elementRange.compareBoundaryPoints(selectedRange.START_TO_END, selectedRange) > -1) {
      
              selectedElements.push(element);
      
            }
          }
        }
      
        elementRange.detach();
      
        return selectedElements;
      
      };
      

      The IE branch looks like this:

      /** 
          @param {Document} doc
          @return {Array.<Element>} 
      */
      getSelectedElements.fromIE = function (doc) {
      
        // Selection - http://msdn.microsoft.com/en-us/library/ie/dd347133(v=vs.85).aspx
        // TextRange - http://msdn.microsoft.com/en-us/library/dd347140(v=vs.85).aspx
        // ControlRange - http://msdn.microsoft.com/en-us/library/ie/ms537447(v=vs.85).aspx
      
        /** @type {TextRange|ControlRange} */
        var ieRange = doc.selection && doc.selection.createRange();
      
        /** @type {Array.<Element>} */
        var selectedElements = [];
      
        /** @type {TextRange} */
        var selectedRange;
      
        /** @type {Element} */
        var containerElement;
      
        /** @type {NodeList} */
        var containedElements;
      
        /** @type {TextRange} */
        var elementRange;
      
        /** @type {Element} */
        var element;
      
        /** @type {Selection} */
        var selection;
      
        /** @type {number} */
        var i = -1;
      
      
        if (ieRange.text === void 0) {
      
          return []; // FIXME: It's a ControlRange, give up.
      
        }
      
        selectedRange = /** @type {TextRange} */ ieRange;
      
        containerElement = selectedRange.parentElement();
      
        containedElements = containerElement.getElementsByTagName('*');
      
        elementRange = doc.body.createTextRange();
      
        while ((element = containedElements[++i])) {
      
            elementRange.moveToElementText(element);
      
            if (elementRange.compareEndPoints("StartToEnd", selectedRange) > -1 && 
                elementRange.compareEndPoints("EndToStart", selectedRange) < 1) {
      
              selectedElements.push(element);
      
            } 
        }
      
        return /** @type {Array.<Element>} */ selectedElements;
      
      };
      

      Now, the issue I want to solve is this: if only part of the text in an element is selected, it appears in the returned array, even though it is only partly selected.

      I'd like to add a parameter that changes the behavior to only include fully-selected elements. I have a feeling the answer lies with compareBoundaryPoints, I just don't understand it well enough to figure it out yet.

      Also, the IE code is untested so far, but please let me know if anything looks wrong with it (or the DOM branch).

      解决方案

      After getting some sleep and reading up on compareBoundaryPoints again, I think I have the answer.

      if (elementRange.compareBoundaryPoints(Range.START_TO_START, selectedRange) > -1 &&
          elementRange.compareBoundaryPoints(Range.END_TO_END, selectedRange) < 1) {
      

      This seems to only evaluate to true for elements that fall completely within the user selection.

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

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