jQuery Establecer la posición del cursor en el área de texto

446

¿Cómo se configura la posición del cursor en un campo de texto usando jQuery? Tengo un campo de texto con contenido y quiero que el cursor de los usuarios esté posicionado en un cierto desplazamiento cuando se enfocan en el campo. El código debería verse así:

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

¿Cómo sería la implementación de esa función setCursorPosition? Si tuviera un campo de texto con el contenido abcdefg, esta llamada haría que el cursor se posicionara de la siguiente manera: abcd ** | ** efg.

Java tiene una función similar, setCaretPosition. ¿Existe un método similar para javascript?

Actualización: modifiqué el código de CMS para que funcione con jQuery de la siguiente manera:

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);
5
  • 78
    $(this).get(0).setSelectionRange)? Sabes que es exactamente lo mismo que this.setSelectionRange, solo que más lento y más difícil de leer, ¿verdad? jQuery no está haciendo literalmente nada por ti aquí. bobince 26/04/10 a las 10:37
  • 2
    Para agregar al comentario de @bobince, la función debe iterar para cada uno de los elementos seleccionados y devolver esto. El código correcto está en mi respuesta. HRJ 6 de septiembre de 2010 a las 11:57
  • 21
    @bobince tampoco es del todo correcto. 'esto' no es el nodo DOM, sino el objeto jQuery. Entonces, $ (this) .get (0) .setSelectionRange es lo mismo que this.get (0) .setSelectionRange, no lo mismo que this.setSelectionRange. Prestaul 7 de septiembre de 2010 a las 23:15
  • $ (this) [0] es más rápido que $ (this) .get (0)EminezArtus 1/10/2014 a las 3:53
  • Consulte este tutorial para obtener una solución completa. webdesignpluscode.blogspot.com/2017/05/…Waqas Ali 28 de mayo de 2017 a las 11:14
262

Tengo dos funciones:

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

Entonces puedes usar setCaretToPos así:

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

Ejemplo en vivo con a textareay an input, que muestra el uso de jQuery:

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

$("#set-textarea").click(function() {
  setCaretToPos($("#the-textarea")[0], 10)
});
$("#set-input").click(function() {
  setCaretToPos($("#the-input")[0], 10);
});
<textarea id="the-textarea" cols="40" rows="4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</textarea>
<br><input type="button" id="set-textarea" value="Set in textarea">
<br><input id="the-input" type="text" size="40" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit">
<br><input type="button" id="set-input" value="Set in input">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

A partir de 2016, probado y funcionando en Chrome, Firefox, IE11, incluso IE8 (ver eso último aquí ; los fragmentos de pila no son compatibles con IE8).

5
  • 3
    ¿Por qué sería necesario colapsar (verdadero) ya que va a establecer las compensaciones de selección de inicio y final? Alexis Wilke 31/12/2013 a las 9:32
  • @mareoraft: funciona en textarea(y input) para mí en Chrome, Firefox, IE8 e IE11. T.J. Crowder 3 de enero de 2016 a las 13:49
  • Parece que no puedo hacer que esto funcione con mi script. Tengo un área de texto que está vacía al cargar la página, luego se llena con javascript cuando se usa la aplicación. Quiero que el símbolo de intercalación vuelva a 0 antes de cada nueva escritura (un registro del uso). ¿Son los datos dinámicos los que me están causando problemas? si es así, ¿cómo podría evitarlo? Chris 3 de junio de 2016 a las 14:58
  • ¿Cuál es el significado del 'carácter' literal de cadena? ¿Es necesario utilizar esa cadena específica? Jon Schneider 31/0317 a las 15:46
  • De acuerdo con createTextRange debe evitarse. developer.mozilla.org/en-US/docs/Web/API/TextRangeJustin 28 jul a las 19:38
302

Aquí hay una solución de 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 esto puedes hacer

$('#elem').selectRange(3,5); // select a range of text
$('#elem').selectRange(3); // set cursor position
10
  • 2
    @Jesse: No sé cómo sucedió, por lo general uso 4. Fijo. mpen 12/03/11 a las 18:49
  • 1
    @UberNeet: actualizado según su sugerencia. mpen 8 de mayo de 2013 a las 20:46
  • 1
    @Enve: No tengo una copia de IE 5.5 para probar, pero probablemente se deba a que jQuery no es compatible con IE 5.5 . mpen 23 de agosto de 2013 a las 23:37
  • 1
    @ JaroslavZáruba: Sí. Está. Pero le permite no tener que escribir selectRange($('.my_input')[0], 3, 5)si ya está usando jQuery. Además, debería funcionar con más de un elemento, si lo necesita, por cualquier motivo. Si desea un idioma nativo puro, utilice la solución de CMS. mpen 10 de enero de 2014 a las 16:39
  • 2
    Necesitaba agregar de $('#elem').focus()antemano para que apareciera el cursor parpadeante. mareoraft 23/11/15 a las 19:09
37

Las soluciones aquí son correctas excepto por el código de extensión jQuery.

La función de extensión debe iterar sobre cada elemento seleccionado y volver thisa admitir el encadenamiento. Aquí es el de una versión correcta:

$.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;
};
1
  • 4
    Cada función devuelve el objeto jquery. para que pueda hacer: return this.each(function...)y eliminar la línea independiente. jhummel 27/04/2014 a las 20:23
24

Encontré una solución que me funciona:

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

Ahora puede mover el foco al final de cualquier elemento llamando a:

$(element).focusEnd();

O especifica la posición.

$(element).setCursorPosition(3); // This will focus on the third character.
2
  • 4
    Para los elementos de área de texto, se agrega una mejora a focusEnd this.scrollTop(this[0].scrollHeight);para garantizar que el área de texto se desplaza para hacer visible el punto de inserción. Drew 31 de mayo de 2015 y 16:31
  • Según Mozilla, simplemente evitaría usar cualquier cosa que use createTextRange- developer.mozilla.org/en-US/docs/Web/API/TextRangeJustin 28 jul a las 19:37
12

Esto funcionó para mí en Safari 5 en Mac OSX, jQuery 1.4:

$("Selector")[elementIx].selectionStart = desiredStartPos; 
$("Selector")[elementIx].selectionEnd = desiredEndPos;
1
  • para mí eso no funcionaba bien con un acceso directo, pero funcionó perfectamente. $ (myID) .prop ('selectionStart', posición); $ (myID) .prop ('fin de selección', posición); amdan 18/04/20 a las 20:47
9

Me doy cuenta de que esta es una publicación muy antigua, pero pensé que debería ofrecer quizás una solución más simple para actualizarla 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;
}

Uso para usar ctrl-enter para agregar una nueva línea (como en 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);
    }
});

Estoy abierto a la crítica. Gracias.

ACTUALIZACIÓN: Esta solución no permite que funcione la funcionalidad normal de copiar y pegar (es decir, ctrl-c, ctrl-v), por lo que tendré que editar esto en el futuro para asegurarme de que esa parte funcione nuevamente. Si tiene una idea de cómo hacer eso, por favor comente aquí, y estaré feliz de probarlo. Gracias.

7

En IE para mover el cursor en alguna posición este código es suficiente:

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

Estoy usando esto: http://plugins.jquery.com/project/jCaret

0
7

¿Establecer el foco antes de haber insertado el texto en el área de texto así?

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

Esto me funciona en Chrome

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

Aparentemente, necesita un retraso de un microsegundo o más, porque generalmente un usuario se enfoca en el campo de texto haciendo clic en alguna posición en el campo de texto (o presionando la pestaña) que desea anular, por lo que debe esperar hasta que la posición sea establecido por el usuario, haga clic en y luego cámbielo.

1
  • La asignación a propiedades de solo lectura no está permitida en modo estrictoIvan Rubinson 9/03/19 a las 11:47
4

Solo recuerde devolver falso justo después de la llamada a la función si está usando las teclas de flecha, ya que Chrome arruina el frack de lo contrario.

{
    document.getElementById('moveto3').setSelectionRange(3,3);
    return false;
}
1
  • 2
    No es una buena práctica return false;. Tu quieres en su event.preventDefault();lugar. Si devuelve falso, está dando a entender event.stopPropagation()que no siempre es deseableAlan H. 8 de mayo de 12 a las 19:12
4

Basado en esta pregunta , la respuesta no funcionará perfectamente para ie y opera cuando haya una nueva línea en el área de texto. La respuesta explica cómo ajustar el selectionStart, selectionEnd antes de llamar a setSelectionRange.

Probé el ajuste AdjustOffset de la otra pregunta con la solución propuesta por @AVProgrammer y funciona.

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

Pequeña modificación al código que encontré en bitbucket

El código ahora puede seleccionar / resaltar con puntos de inicio / finalización si se le dan 2 posiciones. Probado y funciona bien en FF / Chrome / IE9 / Opera.

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

El código se enumera a continuación, solo se cambiaron algunas líneas:

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

Tenía que hacer que esto funcionara para elementos satisfactorios y jQuery y pensé que alguien podría quererlo listo para usar:

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

El uso $(selector).getCaret()devuelve el desplazamiento numérico y $(selector).setCaret(num)establece el desplazamiento y fija el foco en el elemento.

También un pequeño consejo, si ejecuta $(selector).setCaret(num)desde la consola, devolverá el archivo console.log pero no visualizará el foco ya que está establecido en la ventana de la consola.

Mejores; D

1

Puede cambiar directamente el prototipo si setSelectionRange no existe.

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

0

En cuanto a mí, aquí la forma más fácil de agregar texto (Tab -> \ t) en el área de texto por posición del cursor y guardar el foco en el cursor:

$('#text').keyup(function () {
    var cursor = $('#text').prop('selectionStart');
    //if cursot is first in textarea
    if (cursor == 0) {
        //i will add tab in line
        $('#text').val('\t' + $('#text').val());
        //here we set the cursor position
        $('#text').prop('selectionEnd', 1);
    } else {
        var value = $('#text').val();
        //save the value before cursor current position
        var valToCur = value.substring(0, cursor);
        //save the value after cursor current position
        var valAfter = value.substring(cursor, value.length);
        //save the new value with added tab in text
        $('#text').val(valToCur + '\t' + valAfter);
        //set focus of cursot after insert text (1 = because I add only one symbol)
        $('#text').prop('selectionEnd', cursor + 1);
    }
});