在选定区域取消选择后获取插入位置

时间:2016-05-26 22:13:16

标签: javascript jquery html rich-text-editor

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

虽然从大多数情况下获取插入位置来生成基于锚点/焦点节点和锚点/焦点偏移等属性的插入位置,但在大多数情况下,我注意到当我突出显示一个文本块时通过在选择范围内单击取消选择,与(例如)“鼠标”相关联的函数调用。事件仍然认为在此期间选择了选择范围。

如果你要在一个名为“mouseup”的函数中设置一个断点,这是明显正确的。 (在下面的代码中显示showCaretPos()),函数调用' 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>
&#13;
&#13;
&#13;

问题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

1 个答案:

答案 0 :(得分:0)

我找到了在html输入元素(浏览器:Chrome)中选定文本内部单击后获取插入符位置的解决方案。不过,我需要使用计时器来破解。这是我的方法。

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

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

如果单击“选择”内的内容,则情况会稍有不同。

首先,在这种特殊情况下,要想获得所有插入符号的位置,必须避免在事件处理程序的早期代码中应用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