满意的文本选择不起作用

时间:2017-03-31 10:16:53

标签: javascript range selection

我面临以下问题:当我尝试在contenteditable元素中选择文本并且选择的结尾是元素内容的开头时,则没有触发select事件且没有{{1 }和Selection个对象。

有人可以就可能出现这种情况的原因或我如何防止这种情况向我提出任何建议吗?

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

Range
$('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;
}

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

GIF illustrating the steps to reproduce the issue

2 个答案:

答案 0 :(得分:6)

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

$('div[contenteditable="true"]').bind("mouseup keyup touchend", // ...

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

有两种解决方法:

  1. 聆听整个body上的活动。缺点是您收到的更多事件不会影响选择,并且仍有可能在页面外发送mouseup个事件。
  2. 聆听selectionchange事件。
  3. document.addEventListener('selectionchange', function(event) {
      console.log(event.type);
    });
    <div contenteditable="true">Hello world!</div>

    您当然可以像在此事件处理程序中一样访问选择。每次选择更改时都会触发此事件,因此您可能需要throttle它。

    可以在下面找到完整的实现。

    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));
    <div contenteditable="true">Hello world!</div>

答案 1 :(得分:1)

您的实际代码运行良好,并在控制台中记录Selection对象,即使选择的结尾是元素内容的开头。

确实,您需要记录此Selection文本,而不是记录整个object,这反映了每个事件的整个Selection对象更改。

我更新了您的代码段以使用Selection.toString()记录所选内容的文本,您可以在此处看到它:

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

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

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

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

  return null;
}
&#13;
<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>
&#13;
&#13;
&#13;

您可以查看this answer,它会显示并说明选择文字的完美方式。