满意的文本选择不起作用

now*_*iko 5 javascript range selection

我面临以下问题:当我尝试在contenteditable元素中选择文本并且选择的结尾是元素内容的开头时,则不会触发select事件并且没有SelectionRange对象.

有人可以告诉我为什么会出现这种情况或如何防止这种情况?

负责获取选择范围的代码:

$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
  lastCaretIndex = getSelectionRange();
});

function getSelectionRange() {
  var sel;
  if (window.getSelection) {
    sel = window.getSelection();

    console.log(sel); // this doesn't print anything event empty string

    if (sel.rangeCount) {
      return sel.getRangeAt(0);
    }
  } else if (document.selection) {
    return document.createRange();
  }

  return null;
}
Run Code Online (Sandbox Code Playgroud)
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

JSFiddle(打开浏览器控制台以确保不会记录选择).

GIF说明了重现问题的步骤

Jus*_*ent 6

问题是您只在contenteditable元素上发生特定事件时记录选择更改.更具体地说,你有

$('div[contenteditable="true"]').bind("mouseup keyup touchend", // ...
Run Code Online (Sandbox Code Playgroud)

特别mouseup是当选择改变时,通常会触发事件.除非它没有.当您将鼠标释放到可编辑范围之外时div(在您的示例中执行!),那么div将永远不会接收mouseup事件,因此永远不会记录选择.

有两种方法:

  1. 倾听整个事件body.缺点是您收到的更多事件不会影响选择,并且仍然可以mouseup在页面外获取事件.
  2. 听取这个selectionchange事件.

document.addEventListener('selectionchange', function(event) {
  console.log(event.type);
});
Run Code Online (Sandbox Code Playgroud)
<div contenteditable="true">Hello world!</div>
Run Code Online (Sandbox Code Playgroud)

您当然可以像在目前的事件处理程序中一样访问选择.每次选择更改时都会触发此事件,因此您可能希望对其进行限制.

完整实现可以在下面找到.

function handler() {
  // do whatever you want here
  // this shows the selection and all ranges it consists of
  var sel = window.getSelection(),
      ranges = Array(sel.rangeCount).fill(0).map((_, i) => sel.getRangeAt(i));
  ranges = ranges.map((r) => `${r.startOffset}-${r.endOffset}`).join(';');
  console.log(`Selection [${ranges}:"${sel.toString()}"]`);
}

function throttle(func) {
  var timeoutId = false,
      called = false,
      wrap = function() {
        if (!called) {
          clearInterval(timeoutId);
          timeoutId = false;
        } else {
          func();
        }
        called = false;
      };
  return function() {
    if (timeoutId === false) {
      func();
      timeoutId = setInterval(wrap, 500);
    } else {
      called = true;
    }
  };
}

document.addEventListener('selectionchange', throttle(handler));
Run Code Online (Sandbox Code Playgroud)
<div contenteditable="true">Hello world!</div>
Run Code Online (Sandbox Code Playgroud)