更改contenteditable后恢复光标位置

时间:2014-10-01 10:40:33

标签: javascript jquery html contenteditable

有这样的HTML:

<div contenteditable="true" class="value research-form thumbnail">
 Some text here
 </div>

div的内容应该动态地突出显示某些单词,而用户类型例如:

 <div contenteditable="true" class="value research-form thumbnail">
 Some text here <span style="background-color: yellow">highlight</div> it
 </div>
 <script>
    $(document).ready(function () {
        var input = $('#textarea').on('input', function (event) {
            var newText = input.text().replace('highlight', '<span style="background-color: yellow">highlight</div>');
            input.html($.parseHTML(newText));
        });
    });
</script>

但是有一个问题:当我在div光标中刷新文本时,在输入文本的开头移动。

有没有办法在更改contenteditable值后恢复光标位置?或者也许还有其他方法可以达到同样的效果?

1 个答案:

答案 0 :(得分:9)

我找到了解决方案。

这是一个完整的代码:

<div class="container" style="margin-top: 10px">

    <div class="thumbnail value" contenteditable="true">

    </div>

</div>

<script>
    $(document).ready(function () {
        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 setCaretPosition(element, offset) {
            var range = document.createRange();
            var sel = window.getSelection();

            //select appropriate node
            var currentNode = null;
            var previousNode = null;

            for (var i = 0; i < element.childNodes.length; i++) {
                //save previous node
                previousNode = currentNode;

                //get current node
                currentNode = element.childNodes[i];
                //if we get span or something else then we should get child node
               while(currentNode.childNodes.length > 0){
                  currentNode = currentNode.childNodes[0];
               }

                //calc offset in current node
                if (previousNode != null) {
                    offset -= previousNode.length;
                }
                //check whether current node has enough length
                if (offset <= currentNode.length) {
                    break;
                }
            }
            //move caret to specified offset
            if (currentNode != null) {
                range.setStart(currentNode, offset);
                range.collapse(true);
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }

        function onInput(event) {
            var position = getCaretCharacterOffsetWithin(input.get(0));
            var text = input.text();
            text = text.replace(new RegExp('\\btest\\b', 'ig'), '<span style="background-color: yellow">test</span>');
            input.html($.parseHTML(text));
            setCaretPosition(input.get(0), position);
        }

        var input = $('.value').on('input',onInput);

        //content should be updated manually to prevent aditional spaces
        input.html('simple input test example');
        //trigger event
        onInput();
    });
</script>