jQuery Cursorposition im Textbereich festlegen

435

Wie setzen Sie die Cursorposition in einem Textfeld mit jQuery? Ich habe ein Textfeld mit Inhalt und möchte, dass der Benutzercursor an einem bestimmten Versatz positioniert wird, wenn er sich auf das Feld konzentriert. Der Code sollte ungefähr so ​​aussehen:

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

Wie würde die Implementierung dieser setCursorPosition-Funktion aussehen? Wenn Sie ein Textfeld mit dem Inhalt abcdefg hätten, würde dieser Aufruf dazu führen, dass der Cursor wie folgt positioniert wird: abcd ** | ** efg.

Java hat eine ähnliche Funktion, setCaretPosition. Gibt es eine ähnliche Methode für Javascript?

Update: Ich habe den CMS-Code so geändert, dass er mit jQuery funktioniert:

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
quelle
78
$(this).get(0).setSelectionRange)? Sie wissen, dass das genau das gleiche ist wie this.setSelectionRange, nur langsamer und schwerer zu lesen, oder? jQuery tut hier buchstäblich nichts für Sie.
Bobince
2
Um den Kommentar zu @bobince hinzuzufügen, sollte die Funktion für jedes der ausgewählten Elemente iterieren und diesen zurückgeben. Der richtige Code ist in meiner Antwort.
HRJ
21
@bobince ist eigentlich auch nicht ganz richtig. 'this' ist nicht der DOM-Knoten, sondern das jQuery-Objekt. $ (This) .get (0) .setSelectionRange ist also dasselbe wie this.get (0) .setSelectionRange, nicht dasselbe wie this.setSelectionRange.
Prestaul
$ (this) [0] ist schneller als $ (this) .get (0)
EminezArtus
In diesem Tutorial finden Sie eine vollständige Lösung. webdesignpluscode.blogspot.com/2017/05/…
Waqas Ali

Antworten:

254

Ich habe zwei Funktionen:

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

Dann können Sie setCaretToPos wie folgt verwenden:

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

Live-Beispiel mit a textareaund an input, das die Verwendung von jQuery zeigt:

Ab 2016 getestet und mit Chrome, Firefox, IE11 und sogar IE8 (siehe das letzte hier ; Stack-Snippets unterstützen IE8 nicht).

CMS
quelle
3
Warum sollte ein Zusammenbruch (wahr) notwendig sein, da Sie die End- und Startauswahl-Offsets festlegen werden?
Alexis Wilke
@mareoraft: Funktioniert auf textarea(und input) für mich unter Chrome, Firefox, IE8 und IE11.
TJ Crowder
Ich scheine nicht in der Lage zu sein, dies mit meinem Skript zum Laufen zu bringen. Ich habe einen Textbereich, der beim Laden der Seite leer ist und dann bei Verwendung der Anwendung mit Javascript gefüllt wird. Ich möchte, dass das Caret vor jedem neuen Schreibvorgang auf 0 zurückgesetzt wird (eine Aufzeichnung der Verwendung). Sind es die dynamischen Daten, die mir Probleme bereiten? Wenn ja, wie könnte ich das umgehen?
Chris
Welche Bedeutung hat das Zeichenfolgenliteral "Zeichen"? Muss diese bestimmte Zeichenfolge verwendet werden?
Jon Schneider
299

Hier ist eine jQuery-Lösung:

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

Damit können Sie tun

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
mpen
quelle
2
@ Jesse: Keine Ahnung, wie das passiert ist, ich benutze normalerweise 4. Behoben.
Mpen
1
@UberNeet: Aktualisiert basierend auf Ihrem Vorschlag.
Mpen
1
@Enve: Ich habe keine Kopie von IE 5.5 zum Testen, aber das liegt wahrscheinlich daran, dass jQuery IE 5.5 nicht unterstützt .
Mpen
1
@ JaroslavZáruba: Ja. Es ist. Sie müssen jedoch nicht schreiben, selectRange($('.my_input')[0], 3, 5)wenn Sie bereits jQuery verwenden. Außerdem sollte es mit mehr als einem Element funktionieren, wenn Sie dies aus irgendeinem Grund benötigen. Wenn Sie reine native möchten, verwenden Sie bitte die CMS-Lösung.
Mpen
2
Ich musste $('#elem').focus()vorher hinzufügen , damit der blinkende Cursor angezeigt wird.
Mareoraft
37

Die Lösungen hier sind bis auf den jQuery-Erweiterungscode richtig.

Die Erweiterungsfunktion sollte über jedes ausgewählte Element iterieren und thiszur Unterstützung der Verkettung zurückkehren. Hier ist die eine korrekte Version:

$.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
quelle
4
Jede Funktion gibt das jquery-Objekt zurück. Sie können also tatsächlich Folgendes tun: return this.each(function...)und die eigenständige Leitung entfernen.
Jhummel
23

Ich habe eine Lösung gefunden, die für mich funktioniert:

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

Jetzt können Sie den Fokus an das Ende eines Elements verschieben, indem Sie Folgendes aufrufen:

$(element).focusEnd();

Oder Sie geben die Position an.

$(element).setCursorPosition(3); // This will focus on the third character.
AVProgrammer
quelle
3
Für Textbereichselemente besteht eine Verbesserung von focusEnd darin, hinzuzufügen this.scrollTop(this[0].scrollHeight);, um sicherzustellen, dass der Textbereich gescrollt wird , um die Einfügemarke sichtbar zu machen.
Drew
12

Dies funktionierte für mich unter Safari 5 unter Mac OS X, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;
BobFromBris
quelle
Für mich funktionierte das mit einem direkten Zugang nicht gut, aber das funktionierte perfekt. $ (myID) .prop ('selectionStart', Position); $ (myID) .prop ('selectionEnd', Position);
Amdan
9

Mir ist klar, dass dies ein sehr alter Beitrag ist, aber ich dachte, ich sollte vielleicht eine einfachere Lösung anbieten, um ihn nur mit jQuery zu aktualisieren.

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

Verwendung für die Verwendung von Strg-Enter zum Hinzufügen einer neuen Zeile (wie bei 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);
    }
});

Ich bin offen für Kritik. Vielen Dank.

UPDATE: Diese Lösung ermöglicht nicht, dass normale Kopier- und Einfügefunktionen funktionieren (z. B. Strg-C, Strg-V). Daher muss ich diese in Zukunft bearbeiten, um sicherzustellen, dass der Teil wieder funktioniert. Wenn Sie eine Idee dazu haben, kommentieren Sie diese bitte hier, und ich werde sie gerne testen. Vielen Dank.

Tofirius
quelle
7

Im IE reicht dieser Code aus, um den Cursor an eine Position zu bewegen:

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

quelle
7

Stellen Sie den Fokus ein, bevor Sie den Text in den Textbereich eingefügt haben.

$("#comments").focus();
$("#comments").val(comments);
Steven Whitby
quelle
6

Das funktioniert bei mir in Chrom

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

Anscheinend benötigen Sie eine Verzögerung von einer Mikrosekunde oder mehr, da sich ein Benutzer normalerweise auf das Textfeld konzentriert, indem er auf eine Position im Textfeld klickt (oder auf die Registerkarte drückt), die Sie überschreiben möchten. Sie müssen also warten, bis die Position erreicht ist vom Benutzer festgelegt klicken und dann ändern.

Hung Tran
quelle
Die Zuweisung zu schreibgeschützten Eigenschaften ist im strengen Modus nicht zulässig
Ivan Rubinson
4

Denken Sie daran, direkt nach dem Funktionsaufruf false zurückzugeben, wenn Sie die Pfeiltasten verwenden, da Chrome den Frack ansonsten auffrischt.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}
erich
quelle
2
Es ist keine bewährte Methode return false;. Du willst event.preventDefault();stattdessen. Wenn Sie falsch zurückgeben, implizieren Sie, event.stopPropagation()was nicht immer wünschenswert ist
Alan H.
4

Basierend auf dieser Frage funktioniert die Antwort nicht perfekt für dh und Oper, wenn es eine neue Zeile im Textbereich gibt. In der Antwort wird erläutert, wie Sie selectionStart, selectionEnd anpassen, bevor Sie setSelectionRange aufrufen.

Ich habe das adjustOffset aus der anderen Frage mit der von @AVProgrammer vorgeschlagenen Lösung ausprobiert und es funktioniert.

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
quelle
4

Kleine Änderung am Code, den ich in Bitbucket gefunden habe

Code kann jetzt mit Start- / Endpunkten auswählen / hervorheben, wenn 2 Positionen angegeben sind. Getestet und funktioniert gut in FF / Chrome / IE9 / Opera.

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

Der Code ist unten aufgeführt, nur wenige Zeilen geändert:

(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)
Zweig
quelle
Läuft in Chrome 39, IE11, Safari 5.1.7, aber nicht in Firefox 34: jsfiddle.net/0t94z82k/6
jbobbins
3

Ich musste dies für inhaltsbearbeitbare Elemente und jQuery zum Laufen bringen und dachte, jemand möchte, dass es einsatzbereit ist:

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

Die Verwendung $(selector).getCaret()gibt den $(selector).setCaret(num)Zahlenversatz zurück, legt den Versatz fest und legt den Fokus auf das Element.

Auch ein kleiner Tipp: Wenn Sie $(selector).setCaret(num)von der Konsole aus ausführen , wird das console.log zurückgegeben, aber Sie werden den Fokus nicht visualisieren, da er im Konsolenfenster eingerichtet ist.

Bests; D.

FGZ
quelle
1

Sie können den Prototyp direkt ändern, wenn setSelectionRange nicht vorhanden ist.

(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 Link

Anoop
quelle