获取文本选择元素(Get elements in text selection)

2019-10-16 15:05发布

我想要得到包含的所有的用户选择的元素(如在DOM 2个范围/ MS TextRanges)。

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

  var elements = [];

  // get elements in the user selection somehow

  return elements;

}

我试着按照蒂姆唐氏优良做到这一点的解决了类似的问题 ,以及一些万盎司和MS文档,有些PPK东西。

这种方法基本上是:


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

  • 限定RangeLikeObject作为DOM范围或IE的TextRange。

  • containerNode是一个节点。

  • containerElement是Element。

  • containedElements是一个节点列表。

  • elementRange是RangeLikeObject。

  • 我们selectedRange是RangeLikeObject。

  • selectedElements是元素的数组。

  • element是一个元素。

  • 我们selection是一个SelectionLikeObject。

  • 设置selection从用户的选择。

  • 设置selectedElements到一个新的数组。

  • 对于每一个selectedRangeselection

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

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

    • 设置containedElements到后裔的名单containerElement

    • 对于每个elementcontainedElements

      • 设置elementRangeelement

      • 如果边界elementRange下降的边界之内selectedRange

        • elementselectedElements

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

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

};

IE的分支看起来是这样的:

/** 
    @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;

};

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

我想补充一点,改变行为只包括完全选择元件的参数。 我有一种感觉,答案在于compareBoundaryPoints,我只是不明白它不够好弄明白呢。

此外,IE的代码是未经测试,到目前为止,但请让我知道是否有任何不妥的地方(或DOM分支)。

Answer 1:

得到一些睡眠和阅读完毕后compareBoundaryPoints再次,我想我找到了答案。

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

这似乎只能评估到true的用户选择中完全落元素。



文章来源: Get elements in text selection