Переместить положение курсора с помощью Javascript?

Я хочу переместить курсор точно на четыре пробела перед его текущим положением, чтобы я мог правильно вставить табуляцию. У меня уже есть вставка HTML в позиции курсора, но когда я вставляю HTML, каретка остается. Я потратил последний час или около того на поиск различных способов сделать это, и я перепробовал множество из них, но я не могу заставить ни один из них работать на меня. Вот самый последний метод, который я пробовал:

function moveCaret(input, distance) {
    if(input.setSelectionRange) {
        input.focus();
        input.setSelectionRange(distance, distance);
    } else if(input.createTextRange) {
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd(distance);
        range.moveStart(distance);
        range.select();
    }
}

Он абсолютно ничего не делает - не перемещает курсор, не выдает никаких ошибок или чего-то еще. Это меня сбивает с толку. И да, я знаю, что указанный выше набор методов (должен) устанавливать курсор в определенную позицию от начала указанного узла (то есть input), но даже это не работает. Итак, что именно я делаю не так и как я могу это сделать правильно?


Изменить: на основе ссылок, которые o.v. при условии, мне удалось сколотить кое-что, что наконец-то что-то делает: выдает ошибку. Ура! Вот новый код:

this.moveCaret = function(distance) {
    if(that.win.getSelection) {
        var range = that.win.getSelection().getRangeAt(0);
        range.setStart(range.startOffset + distance);
    } else if (that.win.document.selection) {
        var range = that.win.document.selection.createRange();
        range.setStart(range.startOffset + distance);
    }
}

Теперь это дает ошибку Uncaught Error: NOT_FOUND_ERR: DOM Exception 8. Есть идеи, почему?


person Elliot Bonneville    schedule 28.05.2012    source источник
comment
Вы пробовали устанавливать element.selectionStart и element.selectionEnd? Насколько я помню, он должен работать кроссбраузерно для ввода и текстовых полей.   -  person Fabrício Matté    schedule 28.05.2012
comment
Я избегал этого, потому что считаю, что он не работает в более ранних версиях IE8. Я все равно посмотрю на это.   -  person Elliot Bonneville    schedule 28.05.2012
comment
Единственная версия IE, которую я тестировал, - это IE9, желаю удачи. :)   -  person Fabrício Matté    schedule 28.05.2012
comment
Да, похоже, selectionStart не работает в более ранних версиях IE, но все равно спасибо. знак равно   -  person Elliot Bonneville    schedule 28.05.2012
comment
Возможный дубликат: stackoverflow.com/questions/1181700 / Это и этот ответ, похоже, получил широкое признание.   -  person Oleg    schedule 28.05.2012
comment
@ElliotBonneville: ммм, о чем that говорится в вашем редактировании? Я не вижу, чтобы это было назначено   -  person Oleg    schedule 28.05.2012
comment
@ o.v .: Извини. that - это ссылка на объект, на котором есть window объект. win - это либо открытое окно, либо стандартное окно.   -  person Elliot Bonneville    schedule 28.05.2012
comment
Ошибка DOM, вероятно, возникает, если новое смещение не существует (находится за концом элемента). Также обратите внимание, что начальное смещение относится к числу от символов начала узла, если выбран текстовый узел, но к количеству узлов от начала, если узел является элементом (я знаю, что сбивает с толку). Я мог бы попытаться написать ответ на этот вопрос, но это довольно сложная задача, особенно если вы хотите охватить случаи форматированным текстом (например, тегами ‹b› и т. Д.), А также простым текстом.   -  person Nico Burns    schedule 28.05.2012
comment
@ElliotBonneville: просматривая другие ответы, на которые я ссылался, у меня сильное впечатление, что необходимо сохранить явную ссылку на контентный элемент. Можете ли вы добавить с помощью своего метода barebones jsfiddle? Это довольно интригующе   -  person Oleg    schedule 28.05.2012
comment
@NicoBurns: Да, я понял, что смещение, выходящее за пределы конца элемента, может вызвать ошибку. Я пробовал использовать разные смещения, но пришел к выводу, что причина ошибки не в этом. И нет, я не планирую писать редактор WYSIWYG, это для чего-то другого. В тексте не будет других элементов (стилевых или иных). Я могу дать вам образец HTML, если он понадобится вам для написания ответа.   -  person Elliot Bonneville    schedule 28.05.2012
comment
@ o.v .: Уже поздно, я посмотрю, смогу ли я добавить его завтра. Спасибо, что проявили интерес.   -  person Elliot Bonneville    schedule 28.05.2012


Ответы (1)


У вас есть фрагмент кода, предназначенный для ввода текста и текстовых полей, а не для contenteditable элементов.

При условии, что весь ваш контент находится в одном текстовом узле и выделенный фрагмент полностью содержится в нем, следующее будет работать во всех основных браузерах, включая IE 6.

Демо: http://jsfiddle.net/9sdrZ/

Код:

function moveCaret(win, charCount) {
    var sel, range;
    if (win.getSelection) {
        // IE9+ and other browsers
        sel = win.getSelection();
        if (sel.rangeCount > 0) {
            var textNode = sel.focusNode;
            var newOffset = sel.focusOffset + charCount;
            sel.collapse(textNode, Math.min(textNode.length, newOffset));
        }
    } else if ( (sel = win.document.selection) ) {
        // IE <= 8
        if (sel.type != "Control") {
            range = sel.createRange();
            range.move("character", charCount);
            range.select();
        }
    }
}
person Tim Down    schedule 28.05.2012
comment
+1, потому что это действительно полезный фрагмент кода. Просто вопрос: почему вы получаете доступ к глобальному через аргумент win? - person Andrea Parodi; 03.02.2014
comment
@AndreaParodi: разрешить использование с iframe. В данном случае нас интересует объект хоста window, а не его другая роль как глобального объекта. Для iframe вы должны вызвать moveCaret(iframeElement.contentWindow, charCount). - person Tim Down; 04.02.2014
comment
Однако, если вам нужно перейти к предыдущему текстовому узлу, это не удастся как sel.focusNode == 0 - person adib; 18.11.2015
comment
@adib: Ага, отсюда оговорка в начале ответа. - person Tim Down; 18.11.2015
comment
Почему-то это приемлемый ответ на Stackoverflow без каких-либо объяснений. - person dmr07; 01.07.2016
comment
@ danm07: Какие объяснения тебе нужны? Методы Range и Selection являются стандартными и хорошо задокументированы. Код короткий и простой. Кроме того, если вы хотите аннотировать его комментариями, вы можете это сделать. Единственные комментарии, которые, как мне кажется, могут быть полезными, - это метки для двух основных веток кода (IE 9+ и другие браузеры, IE 6-8). - person Tim Down; 01.07.2016
comment
Предоставление готового решения мало помогает восполнить пробелы в знаниях. Было бы неплохо, если бы вы указали, что пошло не так в коде вопрошающего, я почти уверен, что это этикет Stacks. - person dmr07; 01.07.2016
comment
@ danm07: Я виноват в сотнях ответов. Я не думаю, что это универсальная практика добавлять текущий комментарий ко всему коду на SO; Я не уверен, что так должно быть. Побуждает ли большее количество рук держать или отговаривать людей от поиска API-интерфейсов браузера, которые я здесь использовал? Я не знаю. Во всяком случае, я добавил в этот ответ небольшую деталь. - person Tim Down; 05.07.2016
comment
Это не работает, если ContentEditable уже содержит несколько текстовых узлов и вам нужно переместить курсор за его пределы. - person bnovc; 21.01.2018
comment
@bnovc: Да. Ответ прямо говорит об этом. - person Tim Down; 23.01.2018
comment
Можно ли проделать такой же трюк только для текстового поля? - person Pablo; 27.09.2018