jQuery Установить положение курсора в текстовой области

435

Как установить позицию курсора в текстовом поле с помощью jQuery? У меня есть текстовое поле с содержимым, и я хочу, чтобы курсор пользователя располагался с определенным смещением, когда он фокусируется на поле. Код должен выглядеть примерно так:

$('#input').focus(function() {
  $(this).setCursorPosition(4);
});

Как будет выглядеть реализация этой функции setCursorPosition? Если у вас есть текстовое поле с содержимым abcdefg, этот вызов приведет к позиционированию курсора следующим образом: abcd ** | ** efg.

Java имеет аналогичную функцию setCaretPosition. Существует ли подобный метод для JavaScript?

Обновление: я изменил код CMS для работы с jQuery следующим образом:

new function($) {
  $.fn.setCursorPosition = function(pos) {
    if (this.setSelectionRange) {
      this.setSelectionRange(pos, pos);
    } else if (this.createTextRange) {
      var range = this.createTextRange();
      range.collapse(true);
      if(pos < 0) {
        pos = $(this).val().length + pos;
      }
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    }
  }
}(jQuery);
jcnnghm
источник
78
$(this).get(0).setSelectionRange)? Вы знаете, что это точно так же this.setSelectionRange, только медленнее и труднее читать, верно? JQuery здесь буквально ничего не делает для вас.
bobince
2
Чтобы добавить комментарий @bobince, функция должна выполнить итерацию для каждого из выбранных элементов и вернуть ее. Правильный код в моем ответе.
HRJ
21
@bobince на самом деле тоже не совсем правильно. this это не узел DOM, а объект jQuery. Таким образом, $ (this) .get (0) .setSelectionRange - это то же самое, что this.get (0) .setSelectionRange, а не this.setSelectionRange.
Prestaul
$ (this) [0] быстрее, чем $ (this) .get (0)
EminezArtus
Проверьте это руководство для полного решения. webdesignpluscode.blogspot.com/2017/05/…
Вакас Али

Ответы:

254

У меня есть две функции:

function setSelectionRange(input, selectionStart, selectionEnd) {
  if (input.setSelectionRange) {
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
  }
  else if (input.createTextRange) {
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  }
}

function setCaretToPos (input, pos) {
  setSelectionRange(input, pos, pos);
}

Тогда вы можете использовать setCaretToPos следующим образом:

setCaretToPos(document.getElementById("YOURINPUT"), 4);

Живой пример с a textareaи an input, демонстрирующий использование из jQuery:

По состоянию на 2016 год тестировался и работал на Chrome, Firefox, IE11, даже на IE8 (см. Последнее здесь ; фрагменты стека не поддерживают IE8).

CMS
источник
3
Зачем нужно коллапс (true), так как вы собираетесь установить конец и начать смещения выделения?
Алексис Уилк
@mareoraft: работает для меня textareainput) на Chrome, Firefox, IE8 и IE11.
TJ Crowder
Кажется, я не могу заставить это работать с моим сценарием. У меня есть текстовая область, которая пуста при загрузке страницы, затем заполнена javascript, поскольку приложение используется. Я хочу, чтобы каретка возвращалась к 0 перед каждой новой записью (запись об использовании). это динамические данные, которые вызывают у меня проблемы? если так, как я мог обойти это?
Крис
Каково значение строкового литерала «символ»? Нужно ли использовать эту конкретную строку?
Джон Шнайдер
299

Вот решение jQuery:

$.fn.selectRange = function(start, end) {
    if(end === undefined) {
        end = start;
    }
    return this.each(function() {
        if('selectionStart' in this) {
            this.selectionStart = start;
            this.selectionEnd = end;
        } else if(this.setSelectionRange) {
            this.setSelectionRange(start, end);
        } else if(this.createTextRange) {
            var range = this.createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        }
    });
};

С этим вы можете сделать

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
mpen
источник
2
@Jesse: Не знаю, как это произошло, я обычно использую 4. Исправлено.
mpen
1
@UberNeet: Обновлено на основе вашего предложения.
mpen
1
@Enve: у меня нет копии IE 5.5 для тестирования, но это, скорее всего, связано с тем, что jQuery не поддерживает IE 5.5 .
mpen
1
@ JaroslavZáruba: Да. Это. Но позволяет вам не писать, selectRange($('.my_input')[0], 3, 5)если вы уже используете jQuery. Кроме того, он должен работать с более чем одним элементом, если вам это нужно, по любой причине. Если вы хотите чистый нативный, пожалуйста, используйте решение CMS.
mpen
2
Мне нужно было добавить $('#elem').focus()заранее, чтобы появился мигающий курсор.
maraoraft
37

Решения здесь правильные, за исключением кода расширения jQuery.

Функция расширения должна перебирать каждый выбранный элемент и возвращаться thisдля поддержки цепочки. Вот правильный вариант:

$.fn.setCursorPosition = function(pos) {
  this.each(function(index, elem) {
    if (elem.setSelectionRange) {
      elem.setSelectionRange(pos, pos);
    } else if (elem.createTextRange) {
      var range = elem.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    }
  });
  return this;
};
HRJ
источник
4
Каждая функция возвращает объект jquery. так что вы можете сделать: return this.each(function...)и удалить автономную линию.
jhummel
23

Я нашел решение, которое работает для меня:

$.fn.setCursorPosition = function(position){
    if(this.length == 0) return this;
    return $(this).setSelection(position, position);
}

$.fn.setSelection = function(selectionStart, selectionEnd) {
    if(this.length == 0) return this;
    var input = this[0];

    if (input.createTextRange) {
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
    } else if (input.setSelectionRange) {
        input.focus();
        input.setSelectionRange(selectionStart, selectionEnd);
    }

    return this;
}

$.fn.focusEnd = function(){
    this.setCursorPosition(this.val().length);
            return this;
}

Теперь вы можете переместить фокус в конец любого элемента, вызвав:

$(element).focusEnd();

Или вы указываете позицию.

$(element).setCursorPosition(3); // This will focus on the third character.
AVProgrammer
источник
3
Для элементов textarea, улучшается focusEnd, чтобы прокручивать текстовую область, this.scrollTop(this[0].scrollHeight);чтобы сделать видимой точку вставки.
Дрю
12

Это сработало для меня в Safari 5 на Mac OSX, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;
BobFromBris
источник
для меня это не работало нормально с прямым доступом, но это работало отлично. $ (myID) .prop ('selectionStart', позиция); $ (myID) .prop ('selectionEnd', position);
amdan
9

Я понимаю, что это очень старая статья, но я подумал, что должен предложить, возможно, более простое решение, чтобы обновить ее, используя только jQuery.

function getTextCursorPosition(ele) {   
    return ele.prop("selectionStart");
}

function setTextCursorPosition(ele,pos) {
    ele.prop("selectionStart", pos + 1);
    ele.prop("selectionEnd", pos + 1);
}

function insertNewLine(text,cursorPos) {
    var firstSlice = text.slice(0,cursorPos);
    var secondSlice = text.slice(cursorPos);

    var new_text = [firstSlice,"\n",secondSlice].join('');

    return new_text;
}

Использование ctrl-enter для добавления новой строки (как в Facebook):

$('textarea').on('keypress',function(e){
    if (e.keyCode == 13 && !e.ctrlKey) {
        e.preventDefault();
        //do something special here with just pressing Enter
    }else if (e.ctrlKey){
        //If the ctrl key was pressed with the Enter key,
        //then enter a new line break into the text
        var cursorPos = getTextCursorPosition($(this));                

        $(this).val(insertNewLine($(this).val(), cursorPos));
        setTextCursorPosition($(this), cursorPos);
    }
});

Я открыт для критики. Спасибо.

ОБНОВЛЕНИЕ: Это решение не позволяет работать нормальной функции копирования и вставки (т.е. ctrl-c, ctrl-v), поэтому мне придется отредактировать это в будущем, чтобы убедиться, что часть работает снова. Если у вас есть идея, как это сделать, пожалуйста, прокомментируйте здесь, и я буду рад проверить это. Спасибо.

tofirius
источник
7

В IE для перемещения курсора на какую-то позицию этого кода достаточно:

var range = elt.createTextRange();
range.move('character', pos);
range.select();

источник
7

Установить фокус перед тем, как вставить текст в текстовое поле?

$("#comments").focus();
$("#comments").val(comments);
Стивен Уитби
источник
6

Это работает для меня в Chrome

$('#input').focus(function() {
    setTimeout( function() {
        document.getElementById('input').selectionStart = 4;
        document.getElementById('input').selectionEnd = 4;
    }, 1);
});

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

Хунг Тран
источник
Назначение свойств только для чтения не допускается в строгом режиме
Иван Рубинсон,
4

Просто не забывайте возвращать false сразу после вызова функции, если вы используете клавиши со стрелками, так как в противном случае Chrome обманывает фрак.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}
Erich
источник
2
Это не лучшая практика return false;. Вы хотите event.preventDefault();вместо этого. Если вы возвращаете false, вы подразумеваете, event.stopPropagation()что не всегда желательно
Алан Х.
4

Основываясь на этом вопроса , ответ не будет работать идеально для ie и opera, когда в текстовой области появится новая строка. В ответе объясняется, как настроить selectionStart, selectionEnd перед вызовом setSelectionRange.

Я попытался настроитьOffset из другого вопроса с решением, предложенным @AVProgrammer, и он работает.

function adjustOffset(el, offset) {
    /* From https://stackoverflow.com/a/8928945/611741 */
    var val = el.value, newOffset = offset;
    if (val.indexOf("\r\n") > -1) {
        var matches = val.replace(/\r\n/g, "\n").slice(0, offset).match(/\n/g);
        newOffset += matches ? matches.length : 0;
    }
    return newOffset;
}

$.fn.setCursorPosition = function(position){
    /* From https://stackoverflow.com/a/7180862/611741 */
    if(this.lengh == 0) return this;
    return $(this).setSelection(position, position);
}

$.fn.setSelection = function(selectionStart, selectionEnd) {
    /* From https://stackoverflow.com/a/7180862/611741 
       modified to fit https://stackoverflow.com/a/8928945/611741 */
    if(this.lengh == 0) return this;
    input = this[0];

    if (input.createTextRange) {
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
    } else if (input.setSelectionRange) {
        input.focus();
        selectionStart = adjustOffset(input, selectionStart);
        selectionEnd = adjustOffset(input, selectionEnd);
        input.setSelectionRange(selectionStart, selectionEnd);
    }

    return this;
}

$.fn.focusEnd = function(){
    /* From https://stackoverflow.com/a/7180862/611741 */
    this.setCursorPosition(this.val().length);
}
Гислен Хивон
источник
4

Небольшая модификация кода, который я нашел в bitbucket

Код теперь может выбирать / выделять с начальной / конечной точки, если дано 2 позиции. Проверено и отлично работает в FF / Chrome / IE9 / Opera.

$('#field').caret(1, 9);

Код указан ниже, изменилось только несколько строк:

(function($) {
  $.fn.caret = function(pos) {
    var target = this[0];
    if (arguments.length == 0) { //get
      if (target.selectionStart) { //DOM
        var pos = target.selectionStart;
        return pos > 0 ? pos : 0;
      }
      else if (target.createTextRange) { //IE
        target.focus();
        var range = document.selection.createRange();
        if (range == null)
            return '0';
        var re = target.createTextRange();
        var rc = re.duplicate();
        re.moveToBookmark(range.getBookmark());
        rc.setEndPoint('EndToStart', re);
        return rc.text.length;
      }
      else return 0;
    }

    //set
    var pos_start = pos;
    var pos_end = pos;

    if (arguments.length > 1) {
        pos_end = arguments[1];
    }

    if (target.setSelectionRange) //DOM
      target.setSelectionRange(pos_start, pos_end);
    else if (target.createTextRange) { //IE
      var range = target.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos_end);
      range.moveStart('character', pos_start);
      range.select();
    }
  }
})(jQuery)
прут
источник
Работает в Chrome 39, IE11, Safari 5.1.7, но не в Firefox 34: jsfiddle.net/0t94z82k/6
jbobbins
3

Я должен был заставить это работать для contenteditable элементов и jQuery и подумал, что кто-то может захотеть, чтобы он был готов к использованию:

$.fn.getCaret = function(n) {
    var d = $(this)[0];
    var s, r;
    r = document.createRange();
    r.selectNodeContents(d);
    s = window.getSelection();
    console.log('position: '+s.anchorOffset+' of '+s.anchorNode.textContent.length);
    return s.anchorOffset;
};

$.fn.setCaret = function(n) {
    var d = $(this)[0];
    d.focus();
    var r = document.createRange();
    var s = window.getSelection();
    r.setStart(d.childNodes[0], n);
    r.collapse(true);
    s.removeAllRanges();
    s.addRange(r);
    console.log('position: '+s.anchorOffset+' of '+s.anchorNode.textContent.length);
    return this;
};

Использование $(selector).getCaret()возвращает смещение числа и $(selector).setCaret(num)устанавливает смещение и устанавливает фокус на элемент.

Также небольшой совет: если вы запустите $(selector).setCaret(num)консоль, он вернет console.log, но вы не будете визуализировать фокус, поскольку он установлен в окне консоли.

Bests; D

FGZ
источник
1

Вы можете напрямую изменить прототип, если setSelectionRange не существует.

(function() {
    if (!HTMLInputElement.prototype.setSelectionRange) {
        HTMLInputElement.prototype.setSelectionRange = function(start, end) {
            if (this.createTextRange) {
                var range = this.createTextRange();
                this.collapse(true);
                this.moveEnd('character', end);
                this.moveStart('character', start);
                this.select();
            }
        }
    }
})();
document.getElementById("input_tag").setSelectionRange(6, 7);

ссылка jsFiddle

Anoop
источник