【问题标题】:Get caret position after deselecting in a selected area在选定区域取消选择后获取插入符号位置
【发布时间】:2016-05-26 22:13:15
【问题描述】:

正如标题所说,在通过单击选定区域取消选择后,我试图在内容可编辑的 div 中找到用户的插入符号位置。

虽然从 Document.getSelection() 调用中根据锚/焦点节点和锚/焦点偏移等属性生成范围来获取插入符号位置在大多数情况下都可以正常工作,但我注意到当我突出显示一个文本块时通过在选择范围内单击取消选择,与(例如)“mouseup”事件相关的函数调用仍然认为在此期间选择了选择范围。

如果您要在调用“mouseup”的函数中放置一个断点(在本例中为以下代码中的 showCaretPos()),这显然是正确的,当文本仍在时,该函数在“mouseup”上调用它处于选中状态。

然而,在“mouseup”相关函数被触发后再次运行一个函数来检查插入符号的位置时,我们会得到插入符号应该在哪里的正确偏移量。

之后,插入符号被放置在用户在他们的选择中单击的位置,我想知道是否有一种方法可以在取消选择后根据节点和偏移量。

基本上是第二个示例链接,但为了粘贴方便而放在一起:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
    <title>Sandbox</title>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
</head>
<body>
Non-editable text. Editable is below:
<div id="test" contenteditable="true">Hello, some <b>bold</b> and <i>italic and <b>bold</b></i> text</div>
<div id="caretPos"></div>
</body>
<script>
    function getCaretCharacterOffsetWithin(element) {
        var caretOffset = 0;
        var doc = element.ownerDocument || element.document;
        var win = doc.defaultView || doc.parentWindow;
        var sel;
        if (typeof win.getSelection != "undefined") {
            sel = win.getSelection();
            if (sel.rangeCount > 0) {
                var range = win.getSelection().getRangeAt(0);
                var preCaretRange = range.cloneRange();
                preCaretRange.selectNodeContents(element);
                preCaretRange.setEnd(range.endContainer, range.endOffset);
                caretOffset = preCaretRange.toString().length;
            }
        } else if ( (sel = doc.selection) && sel.type != "Control") {
            var textRange = sel.createRange();
            var preCaretTextRange = doc.body.createTextRange();
            preCaretTextRange.moveToElementText(element);
            preCaretTextRange.setEndPoint("EndToEnd", textRange);
            caretOffset = preCaretTextRange.text.length;
        }
        return caretOffset;
    }

    function showCaretPos() {
        var el = document.getElementById("test");
        var caretPosEl = document.getElementById("caretPos");
        caretPosEl.innerHTML = "Caret position: " + getCaretCharacterOffsetWithin(el);
    }

    document.body.onkeyup = showCaretPos;
    document.body.onmouseup = showCaretPos;
</script>
</html>

来自问题Get a range's start and end offset's relative to its parent container的示例1

JSFiddle:http://jsfiddle.net/TjXEG/900/

来自问题Javascript: How to detect if a word is highlighted的示例2

JSFiddle http://jsfiddle.net/timdown/SW54T/

来自问题Get caret position in contentEditable div的示例3

【问题讨论】:

    标签: javascript jquery html rich-text-editor


    【解决方案1】:

    在 html 输入元素(浏览器:Chrome)中的选定文本内单击后,我找到了一种获取插入符号位置的解决方案。不过,我需要一个带有计时器的黑客来做到这一点。方法如下。

    我将 html 输入元素称为 field。 在本文中,“选择”一词表示字段内的选定部分。它可以是字段中的整个字符串,也可以只是其中的一部分。

    如果您从选择中单击左侧或右侧的文本,一切都会按预期进行。选定的部分会被取消选择,并且在您单击的位置会出现一个插入符号。 您可以让您的事件处理程序返回 field.selectionStart,这是新的插入符号位置,field.selectionEnd 等于 field.selectionStart

    在单击 INSIDE 选择的情况下,情况略有不同,我将在下面解释。

    在我开始重要提醒之前。如果要在事件处理程序中应用解决方案,请务必禁用或删除前面代码行中的任何 event.preventDefault() 语句。如果您仍然使用此语句并单击选择,它将保持原位并且根本不会出现插入符号。

    我们开始吧。

    如果您在选择中单击内部,则视觉结果与单击外部相同:取消选择和插入符号。但是,field.selectionEndfield.selectionStart 现在继续返回值,就好像选择仍然存在一样。 似乎没有办法解决这个问题。我在当前使用的事件处理程序之后注册了一个事件处理程序。我还向容器 html 元素注册了一个事件处理程序,事件在该元素中冒泡(我使用了 body 元素)。两者都无济于事。

    看起来“将插入符号放在已选择文本内的点击位置”是我们无法影响的原生 Chrome 浏览器行为。也许它在所有事件处理程序完成后执行。也许所有事件处理程序都使用某种包含“旧”值的目标元素的独立副本。或者完全是另外一回事。

    解决方案的关键是在一段时间后使用计时器功能处理特定的字段元素。

    然后该字段将自行更新,并产生与您看到的内容相对应的值。

    不过,我的解决方案归类为 hack。我希望有一天有人会想出一个没有计时器的更强大的解决方案。

    这里有一些示例代码,您可以尝试一下。

    /// global function; "e" equals event object
    var clickFieldWithSelection = function(e){ 
        console.log('this.selectionStart:', this.selectionStart, 'e.type:', e.type)
    }
    
    /// code snippet in event handler
    
    //e.preventDefault(); // this line must be disabled or completely deleted 
    var isSingleClick = e.detail === 1;
    var hasSelection = this.selectionStart < this.selectionEnd ;
    // at the end of all keyboard and mouse actions the current selectionStart and selectionEnd are logged in object "this.last"
    var isSelectionPreviousEqualsCurrent = this.selectionStart === this.last.selectionStart && this.selectionEnd === this.last.selectionEnd ;
    var isClickInsideSelection = isSingleClick && hasSelection && isSelectionPreviousEqualsCurrent ;
    // click outside selection can be handled directly, without timer
    if (isClickInsideSelection){
        // bind is needed to pass values to function; 200 ms seems not too fast not too slow
        setTimeout(_fxClickFieldWithSelection.bind(this, e), 200);
        return
    }
    // continue event handler
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多