从js中的窗口选择中删除空白

Remove whitespace from window selection in js

本文关键字:删除 空白 选择 js 窗口      更新时间:2023-09-26

我正试图从用户选择的文本中删除前导或尾随(有时两者都有)空白。根据这个答案执行。这适用于简单的情况,但是,当所选文本包含标记或 时,它会失败。

示例:在小提琴中,尝试从右到左高亮显示-- hi this is bob.,包括末端的空格,然后按Trim。

这导致:

Uncaught IndexSizeError: Failed to execute 'setEnd' on 'Range': The offset 24 is larger than or equal to the node's length (5).

我想可以捕捉到这一点

 if (method == range.setEnd  && range.startOffset + ind >= range.endContainer.length)

但我不知道该怎么处理。我也试着用替换硬盘空间

e2 = document.getElementById('e2');
e2.innerHTML = e2.innerHTML.replace(/ /gi, ' ');

但是,这会使选择为空。代码:

function removeWsFromSelection(fromStart) {
  selection = window.getSelection();
  range = selection.getRangeAt(0);
  if (fromStart) {
    regex = /[^'s]/;
    container = range.startContainer;
    method = range.setStart;
  } else {
    regex = /'s+$/;
    container = range.endContainer;
    method = range.setEnd;
  }
  match = regex.exec(selection.toString());
  if (match) {
    ind = match.index;
    if (ind > 0) {
      // ind is the first non-ws char from the start or first ws char from the end,
      // hence (startOffset + ind)
      method.call(range, container, range.startOffset + ind);
      rng = range.cloneRange();
      selection.removeAllRanges();
      selection.addRange(rng);
    }
  }
}

顺便说一句,不幸的是,Selection.modify不适合我,而且它被认为是非标准的。

如果您对范围修改很满意,您可以检查从开始到结束的修剪符号的长度,然后修改范围的startOffset和endOffset。但对于startContainer和endContainer不是同一节点的情况,这肯定不是一个银弹。至少它适用于某些情况。

const sel = window.getSelection();
const text = sel.toString();
const range = sel.getRangeAt(0);
const startOffset = text.length - text.trimStart().length;
const endOffset = text.length - text.trimEnd().length;
if (startOffset) {
  range.setStart(range.startContainer, range.startOffset + startOffset);
}
if (endOffset) {
  range.setEnd(range.endContainer, range.endOffset - endOffset);
}

这很难看,不能处理一般情况,但似乎有效:

function removeWsFromSelection(fromStart) {
      selection = window.getSelection();
      range = selection.getRangeAt(0);
      if (fromStart) {
        regex = /[^'s]/;
        container = range.startContainer;
        method = range.setStart;
      }
      else {
        regex = /'s+$/;
        container = range.endContainer;
        method = range.setEnd;
      }
      match = regex.exec(selection.toString());
      if (match) {
        ind = match.index;
        if (ind > 0) {
            // ind is the first non-ws char from the start or first ws char from the end,
            // hence (startOffset + ind)
            if (method == range.setEnd  && range.startOffset + ind >= range.endContainer.length) {
              match = regex.exec(range.endContainer.textContent);
              if (match) {
                range.setEnd(range.endContainer, match.index); 
              }
            }
            else {
              method.call(range, container, range.startOffset + ind);
            }
            rng = range.cloneRange();
            selection.removeAllRanges();
            selection.addRange(rng);
        }
      }
    }

我修改了Micheal的答案,使其能够处理多个范围和跨节点边界的范围。这似乎在我所有的测试中都能始终如一地减少选择,但我确信总有边缘情况的空间。

TypeScript,但很容易适应普通JS。

export function trimRanges(selection: Selection) {
    for (let i = 0, range = selection.getRangeAt(0); i < selection.rangeCount; range = selection.getRangeAt(i++)) {
        const text = selection.toString();
        const startOffset = text.length - text.trimStart().length;
        const endOffset = text.length - text.trimEnd().length;
        if (startOffset) {
            const offset = range.startOffset + startOffset;
            if (offset < 0) {
                // If the range will underflow the current element, then it belongs in the previous element
                const start = range.startContainer.parentElement.previousSibling;
                range.setStart(start, start.textContent.length + offset);
            } else if (offset > range.startContainer.textContent.length) {
                // If the range will overflow the current element, then it belongs in the next element
                const start = range.startContainer.parentElement.nextSibling;
                range.setStart(start, offset - range.startContainer.textContent.length);
            } else {
                range.setStart(range.startContainer, offset);
            }
        }
        if (endOffset) {
            const offset = range.endOffset - endOffset;
            if (offset < 0) {
                // If the range will underflow the current element, then it belongs in the previous element
                const end = range.endContainer.parentElement.previousSibling;
                range.setEnd(end, end.textContent.length + offset);
            } else if (offset > range.endContainer.textContent.length) {
                // If the range will overflow the current element, then it belongs in the next element
                const end = range.endContainer.parentElement.nextSibling;
                range.setEnd(end, offset - range.endContainer.textContent.length);
            } else {
                range.setEnd(range.endContainer, offset);
            }
        }
    }
}

请注意,这确实会修改附加到所选内容的范围。如果你需要原子性,你可以假设在编辑之前克隆每个范围,删除所有范围,然后将克隆的范围添加回选择中,但我没有发现这种方法有任何问题。