【问题标题】:update textarea value, but keep cursor position更新 textarea 值,但保持光标位置
【发布时间】:2011-03-18 05:27:22
【问题描述】:

我有一个 html 文本区域,它将通过 javascript 定期更新。

当我这样做时:

$("#textarea").val(new_val);

光标移动到文本末尾。

我想在不改变光标位置的情况下更新文本。此外,如果用户选择了一系列文本,则应保留突出显示。

【问题讨论】:

    标签: javascript html


    【解决方案1】:

    这是在所有主流浏览器的文本区域中获取和设置选择/插入符号位置的一对函数。

    注意:如果您不需要支持 IE selectionStart 和 selectionEnd 属性 (MDN)。下面所有复杂的代码都是为了支持旧版本的 IE。

    function getInputSelection(el) {
        var start = 0, end = 0, normalizedValue, range,
            textInputRange, len, endRange;
    
        if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
            start = el.selectionStart;
            end = el.selectionEnd;
        } else {
            range = document.selection.createRange();
    
            if (range && range.parentElement() == el) {
                len = el.value.length;
                normalizedValue = el.value.replace(/\r\n/g, "\n");
    
                // Create a working TextRange that lives only in the input
                textInputRange = el.createTextRange();
                textInputRange.moveToBookmark(range.getBookmark());
    
                // Check if the start and end of the selection are at the very end
                // of the input, since moveStart/moveEnd doesn't return what we want
                // in those cases
                endRange = el.createTextRange();
                endRange.collapse(false);
    
                if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
                    start = end = len;
                } else {
                    start = -textInputRange.moveStart("character", -len);
                    start += normalizedValue.slice(0, start).split("\n").length - 1;
    
                    if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
                        end = len;
                    } else {
                        end = -textInputRange.moveEnd("character", -len);
                        end += normalizedValue.slice(0, end).split("\n").length - 1;
                    }
                }
            }
        }
    
        return {
            start: start,
            end: end
        };
    }
    
    function offsetToRangeCharacterMove(el, offset) {
        return offset - (el.value.slice(0, offset).split("\r\n").length - 1);
    }
    
    function setInputSelection(el, startOffset, endOffset) {
        if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
            el.selectionStart = startOffset;
            el.selectionEnd = endOffset;
        } else {
            var range = el.createTextRange();
            var startCharMove = offsetToRangeCharacterMove(el, startOffset);
            range.collapse(true);
            if (startOffset == endOffset) {
                range.move("character", startCharMove);
            } else {
                range.moveEnd("character", offsetToRangeCharacterMove(el, endOffset));
                range.moveStart("character", startCharMove);
            }
            range.select();
        }
    }
    

    当你改变textarea的值时,先保存选择,然后再恢复:

    var t = document.getElementById("textarea");
    var sel = getInputSelection(t);
    t.value = some_new_value;
    setInputSelection(t, sel.start, sel.end);
    

    【讨论】:

    • setInputSelection 函数是否仅在 IE 中有效?我问是因为 createTextRange 方法在其他浏览器中不存在(对于输入元素),所以会抛出错误。
    • 我使用您的代码创建了一个演示。代码在这里:vidasp.net/js/selection.js 演示在这里:vidasp.net/tinydemos/select-demo.html 该演示在 IE9 beta、Chrome 和 Safari 中完美运行。但是,Firefox 中存在一个问题:如果您以编程方式设置选择(selec.set(input, 10, 20); 它不会起作用除非您聚焦文本框(input.focus();)。在我的演示中,我关注的是设置选择后的文本框 - 只是为了让它在 Firefox 中工作。因此,请考虑将 el.focus() 放在 setInputSelection 函数的末尾。
    • 这仍然是处理此问题的最佳方法吗?我在一个较旧的项目中遇到了这段代码,对于这样一个看似简单的任务,它似乎真的很长/很复杂
    • @Marie:IE selectionStart 和 selectionEnd
    • 一个注释 - typeof NaN === 'number' 出现在 true。这恰好没有达到边界情况,但如果有人乱用代码,他们应该知道处理这种情况会更强大。
    【解决方案2】:

    十年后,但这就是我想出的用于替换文本区域中的项目的方法。当替换为更长或更短的文本时,需要一些额外的处理来调整插入符号或选择。

    // find and replace in textarea while preserving caret and selection
    function replaceText(el, findText, replaceWithText) {
        var text = el.value;
        var selectionStart = 0;
        var selectionEnd = 0;
      // only support modern browsers for preserving caret and selection
        if (el.setSelectionRange) {
            selectionStart = el.selectionStart;
            selectionEnd = el.selectionEnd;
        }
        var start = 0;
        while ((start = text.indexOf(findText, start)) > -1) {
            var end = start + findText.length;
            text = text.substr(0, start) + replaceWithText + text.substr(end);
            if (selectionStart < end) {
                selectionStart = Math.min(selectionStart, start + replaceWithText.length);
            } else {
                selectionStart = selectionStart + replaceWithText.length - (end - start);
            }
            if (selectionEnd < end) {
                selectionEnd = Math.min(selectionEnd, start + replaceWithText.length);
            } else {
                selectionEnd = selectionEnd + replaceWithText.length - (end - start);
            }
            start += replaceWithText.length;
        }
      // don't do anything unless we need to (otherwise destroys undo)
        if (el.value != text) { 
            el.value = text;
            if (el.setSelectionRange) {
                el.selectionStart = selectionStart;
                el.selectionEnd = selectionEnd;
            }
        }
    }
    Place caret on or after the word LONGER, or select some text after or including it:
    <br />
    <textarea id='t'>Here is
    some LONGERtext
    to replace</textarea>
    <br />
    <input type="button" onclick="replaceText(document.getElementById('t'),'LONGER',''); document.getElementById('t').focus();" value="remove word LONGER" />

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-16
      • 1970-01-01
      • 1970-01-01
      • 2017-01-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多