jQuery Ustaw pozycję kursora w polu tekstowym

435

Jak ustawić pozycję kursora w polu tekstowym za pomocą jQuery? Mam pole tekstowe z zawartością i chcę, aby kursor użytkowników był ustawiony z pewnym przesunięciem, gdy skupią się na polu. Kod powinien wyglądać mniej więcej tak:

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

Jak wyglądałoby wdrożenie tej funkcji setCursorPosition? Jeśli masz pole tekstowe z zawartością abcdefg, to wywołanie spowoduje ustawienie kursora w następujący sposób: abcd ** | ** efg.

Java ma podobną funkcję, setCaretPosition. Czy istnieje podobna metoda dla javascript?

Aktualizacja: zmodyfikowałem kod CMS do pracy z jQuery w następujący sposób:

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
źródło
78
$(this).get(0).setSelectionRange)? Wiesz, że to dokładnie to samo this.setSelectionRange, tylko wolniejsze i trudniejsze do odczytania, prawda? jQuery nie robi dla ciebie dosłownie nic.
bobince
2
Aby dodać do komentarza @bobince, funkcja powinna iterować każdy z wybranych elementów i zwrócić to. Prawidłowy kod jest w mojej odpowiedzi.
HRJ
21
@ Bobin też nie jest całkiem poprawny. „to” nie jest węzłem DOM, ale obiektem jQuery. Zatem $ (this) .get (0) .setSelectionRange jest taki sam jak this.get (0) .setSelectionRange, a nie taki sam jak this.setSelectionRange.
Prestaul
$ (this) [0] jest szybszy niż $ (this) .get (0)
EminezArtus
Sprawdź ten samouczek, aby uzyskać pełne rozwiązanie. webdesignpluscode.blogspot.com/2017/05/…
Waqas Ali

Odpowiedzi:

254

Mam dwie funkcje:

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);
}

Następnie możesz użyć setCaretToPos w następujący sposób:

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

Przykład na żywo z a textareai an input, pokazujący użycie z jQuery:

Od 2016 r. Przetestowane i działające na przeglądarkach Chrome, Firefox, IE11, a nawet IE8 (zobacz to ostatnie tutaj ; Fragmenty stosu nie obsługują IE8).

CMS
źródło
3
Dlaczego zwinięcie (prawda) byłoby konieczne, skoro zamierzasz ustawić koniec i przesunąć zaznaczenie?
Alexis Wilke,
@mareoraft: Działa na textarea(i input) dla mnie w Chrome, Firefox, IE8 i IE11.
TJ Crowder
Wydaje mi się, że nie jestem w stanie zmusić tego do pracy z moim skryptem. Mam obszar tekstowy, który jest pusty podczas ładowania strony, a następnie wypełniony javascript, gdy aplikacja jest używana. Chcę, aby karetka była zwracana do zera przed każdym nowym zapisem (zapis użycia). czy to dynamiczne dane powodują mi problemy? jeśli tak, jak mogę to obejść?
Chris
Jakie znaczenie ma literowy ciąg „znaków”? Czy ten konkretny ciąg musi zostać użyty?
Jon Schneider
299

Oto rozwiązanie 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();
        }
    });
};

Dzięki temu możesz to zrobić

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
mpen
źródło
2
@Jesse: Nie wiem jak to się stało, zwykle używam 4. Naprawiono.
mpen
1
@UberNeet: zaktualizowano na podstawie Twojej sugestii.
mpen
1
@Enve: Nie mam kopii IE 5.5 do testowania, ale prawdopodobnie byłoby tak, ponieważ jQuery nie obsługuje IE 5.5 .
mpen 23.08.13
1
@ JaroslavZáruba: Tak. To jest. Ale pozwala ci nie pisać, selectRange($('.my_input')[0], 3, 5)jeśli już używasz jQuery. Ponadto powinien działać z więcej niż jednym elementem, jeśli jest to potrzebne, z jakiegokolwiek powodu. Jeśli chcesz mieć czystą natywność, skorzystaj z rozwiązania CMS.
mpen
2
Musiałem $('#elem').focus()wcześniej dodać, aby pojawił się migający kursor.
mareoraft,
37

Rozwiązania tutaj są słuszne, z wyjątkiem kodu rozszerzenia jQuery.

Funkcja rozszerzenia powinna iterować po każdym wybranym elemencie i powrócić thisdo tworzenia łańcuchów pomocniczych. Oto poprawna wersja:

$.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
źródło
4
Każda funkcja zwraca obiekt jquery. więc możesz zrobić: return this.each(function...)i usunąć samodzielną linię.
jhummel
23

Znalazłem rozwiązanie, które działa dla mnie:

$.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;
}

Teraz możesz przenieść fokus na koniec dowolnego elementu, wywołując:

$(element).focusEnd();

Lub określasz pozycję.

$(element).setCursorPosition(3); // This will focus on the third character.
AVProgrammer
źródło
3
W przypadku elementów pola tekstowego ulepszeniem focusEnd jest dodanie this.scrollTop(this[0].scrollHeight);, aby zapewnić przewijanie obszaru tekstowego , aby widoczny był punkt wstawiania.
Drew
12

Działa to dla mnie w Safari 5 na Mac OSX, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;
BobFromBris
źródło
dla mnie to nie działało dobrze z bezpośrednim dostępem, ale działało idealnie. $ (myID) .prop („selectionStart”, pozycja); $ (myID) .prop ('selectionEnd', position);
amdan
9

Zdaję sobie sprawę, że jest to bardzo stary post, ale pomyślałem, że powinienem zaoferować być może prostsze rozwiązanie, aby zaktualizować go przy użyciu tylko 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;
}

Sposób użycia ctrl-enter w celu dodania nowej linii (jak na Facebooku):

$('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);
    }
});

Jestem otwarty na krytykę. Dziękuję Ci.

AKTUALIZACJA: To rozwiązanie nie pozwala na działanie normalnych funkcji kopiowania i wklejania (tj. Ctrl-c, ctrl-v), więc będę musiał to edytować w przyszłości, aby upewnić się, że ta część znowu działa. Jeśli masz pomysł, jak to zrobić, skomentuj tutaj, a chętnie go przetestuję. Dzięki.

tofirius
źródło
7

W IE, aby przesunąć kursor na jakąś pozycję, wystarczy ten kod:

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

źródło
7

Czy w ten sposób ustawić fokus, zanim wstawisz tekst do obszaru tekstowego?

$("#comments").focus();
$("#comments").val(comments);
Steven Whitby
źródło
6

Działa to dla mnie w chrome

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

Najwyraźniej potrzebujesz opóźnienia w mikrosekundach lub więcej, ponieważ zwykle użytkownik skupia się na polu tekstowym, klikając dowolną pozycję w polu tekstowym (lub naciskając klawisz Tab), którą chcesz zastąpić, więc musisz poczekać, aż pozycja będzie ustawione przez użytkownika kliknij, a następnie zmień.

Hung Tran
źródło
Przypisanie właściwości tylko do odczytu nie jest dozwolone w trybie ścisłym
Ivan Rubinson
4

Pamiętaj tylko, aby zwracać wartość false zaraz po wywołaniu funkcji, jeśli używasz klawiszy strzałek, ponieważ w przeciwnym razie Chrome przerabia frack.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}
erich
źródło
2
Nie jest to najlepsza praktyka return false;. Zamiast event.preventDefault();tego chcesz . Jeśli zwrócisz fałsz, sugerujesz, event.stopPropagation()co nie zawsze jest pożądane
Alan H.
4

W oparciu o to pytanie odpowiedź nie zadziała idealnie dla ie i opery, gdy w polu tekstowym pojawi się nowa linia. W odpowiedzi wyjaśniono, jak dostosować selectionStart, selectionEnd przed wywołaniem setSelectionRange.

Próbowałem wyregulować opcję z drugiego pytania z rozwiązaniem zaproponowanym przez @AVProgrammer i działa.

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);
}
Ghislain Hivon
źródło
4

Mała modyfikacja kodu, którą znalazłem w bitbucket

Kod może teraz wybierać / wyróżniać za pomocą punktów początkowych / końcowych, jeśli otrzyma 2 pozycje. Testowane i działa dobrze w FF / Chrome / IE9 / Opera.

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

Kod jest wymieniony poniżej, zmieniono tylko kilka wierszy:

(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)
Gałązka
źródło
Działa w Chrome 39, IE11, Safari 5.1.7, ale nie w Firefox 34: jsfiddle.net/0t94z82k/6
jbobbins
3

Musiałem sprawić, by działało to w przypadku elementów zadowalających i jQuery, i pomyślałem, że ktoś może chcieć, aby był gotowy do użycia:

$.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;
};

Użycie $(selector).getCaret()zwraca przesunięcie liczby i $(selector).setCaret(num)ustanawia przesunięcie i ustawia fokus na elemencie.

Również mała wskazówka, jeśli uruchomisz $(selector).setCaret(num)z konsoli, zwróci plik console.log, ale nie zwizualizujesz fokusa, ponieważ jest on ustalany w oknie konsoli.

Bests; D

FGZ
źródło
1

Możesz bezpośrednio zmienić prototyp, jeśli setSelectionRange nie istnieje.

(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);

Link do jsFiddle

Anoop
źródło