jQuery Imposta la posizione del cursore nell'area di testo


435

Come si imposta la posizione del cursore in un campo di testo usando jQuery? Ho un campo di testo con contenuti e voglio che il cursore degli utenti sia posizionato su un certo offset quando si concentrano sul campo. Il codice dovrebbe apparire in questo modo:

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

Come sarebbe l'implementazione di quella funzione setCursorPosition? Se avessi un campo di testo con il contenuto abcdefg, questa chiamata comporterebbe il posizionamento del cursore come segue: abcd ** | ** efg.

Java ha una funzione simile, setCaretPosition. Esiste un metodo simile per javascript?

Aggiornamento: ho modificato il codice CMS per funzionare con jQuery come segue:

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

78
$(this).get(0).setSelectionRange)? Sai che è esattamente lo stesso di this.setSelectionRange, solo più lento e più difficile da leggere, giusto? jQuery non sta facendo letteralmente niente per te qui.
bobince,

2
Per aggiungere al commento @bobince, la funzione dovrebbe iterare per ciascuno degli elementi selezionati e restituirlo. Il codice corretto è nella mia risposta.
HRJ,

21
@bobince non è nemmeno del tutto corretto. 'this' non è il nodo DOM, ma l'oggetto jQuery. Quindi, $ (this) .get (0) .setSelectionRange è uguale a this.get (0) .setSelectionRange, non uguale a this.setSelectionRange.
Prestaul,

$ (questo) [0] è più veloce di $ (questo) .get (0)
EminezArtus

Dai un'occhiata a questo tutorial per una soluzione completa. webdesignpluscode.blogspot.com/2017/05/…
Waqas Ali,

Risposte:


254

Ho due funzioni:

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

Quindi puoi usare setCaretToPos in questo modo:

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

Esempio live con a textareae an input, che mostra l'uso di jQuery:

A partire dal 2016, testato e funzionante su Chrome, Firefox, IE11, persino IE8 (vedi l'ultimo qui ; Stack Snippet non supporta IE8).


3
Perché il collasso (vero) sarebbe necessario in quanto hai intenzione di impostare la fine e iniziare gli offset di selezione?
Alexis Wilke,

@mareoraft: funziona su textarea(e input) per me su Chrome, Firefox, IE8 e IE11.
TJ Crowder,

Non riesco a farlo funzionare con la mia sceneggiatura. Ho un'area di testo vuota al caricamento della pagina, quindi riempita da javascript quando viene utilizzata l'applicazione. Voglio che il punto di inserimento venga restituito a 0 prima di ogni nuova scrittura (un record dell'uso). sono i dati dinamici che mi stanno causando problemi? se sì, come potrei aggirarlo?
Chris,

Qual è il significato del "carattere" letterale di stringa? È necessario utilizzare quella stringa specifica?
Jon Schneider,

299

Ecco una soluzione 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();
        }
    });
};

Con questo, puoi farlo

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position

2
@Jesse: Non so come sia successo, di solito uso 4. Risolto.
aprono il

1
@UberNeet: aggiornato in base al tuo suggerimento.
mpen

1
@Enve: non ho una copia di IE 5.5 da testare, ma probabilmente sarebbe perché jQuery non supporta IE 5.5 .
Aprire il

1
@ JaroslavZáruba: Sì. È. Ma ti consente di non dover scrivere selectRange($('.my_input')[0], 3, 5)se stai già utilizzando jQuery. Inoltre, dovrebbe funzionare con più di un elemento, se necessario, per qualsiasi motivo. Se desideri un nativo puro, utilizza la soluzione CMS.
Aprire il

2
Ho dovuto aggiungere in $('#elem').focus()anticipo per far apparire il cursore lampeggiante.
mareoraft,

37

Le soluzioni qui sono esatte tranne il codice di estensione jQuery.

La funzione di estensione dovrebbe iterare su ogni elemento selezionato e tornare thisa supportare il concatenamento. Ecco l' una versione corretta:

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

4
Ogni funzione restituisce l'oggetto jquery. così puoi effettivamente fare: return this.each(function...)e rimuovere la linea autonoma.
jhummel,

23

Ho trovato una soluzione che funziona per me:

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

Ora puoi spostare lo stato attivo alla fine di qualsiasi elemento chiamando:

$(element).focusEnd();

Oppure si specifica la posizione.

$(element).setCursorPosition(3); // This will focus on the third character.

3
Per gli elementi textarea, è necessario aggiungere focusEnd this.scrollTop(this[0].scrollHeight);per garantire che l'area di testo venga fatta scorrere per rendere visibile il punto di inserimento.
Disegnò

12

Questo ha funzionato per me su Safari 5 su Mac OSX, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;

per me che non funzionava bene con un accesso diretto, ma funzionava perfettamente. $ (myID) .prop ('selectionStart', posizione); $ (myID) .prop ('selectionEnd', posizione);
amdan,

9

Mi rendo conto che questo è un post molto vecchio, ma ho pensato che avrei dovuto offrire forse una soluzione più semplice per aggiornarlo usando solo 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;
}

Utilizzo per l'uso di ctrl-invio per aggiungere una nuova riga (come in 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);
    }
});

Sono aperto alla critica. Grazie.

AGGIORNAMENTO: Questa soluzione non consente il normale funzionamento di copia e incolla (ad es. Ctrl-c, ctrl-v), quindi dovrò modificarlo in futuro per assicurarmi che la parte funzioni di nuovo. Se hai un'idea di come farlo, ti preghiamo di commentare qui, e sarò felice di provarlo. Grazie.


7

In IE per spostare il cursore su qualche posizione questo codice è sufficiente:

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


7

Impostare lo stato attivo prima di aver inserito il testo nell'area di testo così?

$("#comments").focus();
$("#comments").val(comments);

6

Questo funziona per me in Chrome

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

Apparentemente hai bisogno di un ritardo di un microsecondo o più, perché di solito un utente si concentra sul campo di testo facendo clic in una posizione nel campo di testo (o colpendo la scheda) che vuoi sovrascrivere, quindi devi aspettare fino a quando la posizione è impostato dall'utente fare clic e quindi modificarlo.


L'assegnazione a proprietà di sola lettura non è consentita in modalità rigorosa
Ivan Rubinson

4

Ricorda di restituire false subito dopo la chiamata di funzione se stai usando i tasti freccia poiché Chrome agita diversamente.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}

2
Non è buona pratica return false;. Tu vuoi event.preventDefault();invece. Se restituisci false, stai insinuando event.stopPropagation()che non è sempre desiderabile
Alan H.

4

Sulla base di questa domanda , la risposta non funzionerà perfettamente per ie e opera quando c'è una nuova riga nell'area di testo. La risposta spiega come regolare selectionStart, selectionEnd prima di chiamare setSelectionRange.

Ho provato a regolareOffOff dall'altra domanda con la soluzione proposta da @AVProgrammer e funziona.

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

Piccola modifica al codice che ho trovato in bitbucket

Il codice è ora in grado di selezionare / evidenziare con i punti di inizio / fine se vengono fornite 2 posizioni. Testato e funziona bene in FF / Chrome / IE9 / Opera.

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

Il codice è elencato di seguito, sono cambiate solo alcune righe:

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

Funziona con Chrome 39, IE11, Safari 5.1.7 ma non con Firefox 34: jsfiddle.net/0t94z82k/6
jbobbins

3

Ho dovuto farlo funzionare per elementi contenti e jQuery e pensavo che qualcuno potesse volerlo pronto per l'uso:

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

L'utilizzo $(selector).getCaret()restituisce l'offset numerico, $(selector).setCaret(num)stabilisce l'offset e imposta lo stato attivo sull'elemento.

Anche un piccolo suggerimento, se si esegue $(selector).setCaret(num)dalla console, verrà restituito console.log ma non verrà visualizzato lo stato attivo poiché è stato stabilito nella finestra della console.

Bests; D


1

È possibile modificare direttamente il prototipo se setSelectionRange non esiste.

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

Collegamento jsFiddle

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.