Friday, June 8, 2012

jQuery Set Cursor Position in Text Area


How do you set the cursor position in a text field using jQuery? I've got a text field with content, and I want the users cursor to be positioned at a certain offset when they focus on the field. The code should look kind of like this:




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



What would the implementation of that setCursorPosition function look like? If you had a text field with the content abcdefg, this call would result in the cursor being positioned as follows: abcd**|**efg.



Java has a similar function, setCaretPosition. Does a similar method exist for javascript?



Update: I modified CMS's code to work with jQuery as follows:




new function($) {
$.fn.setCursorPosition = function(pos) {
if ($(this).get(0).setSelectionRange) {
$(this).get(0).setSelectionRange(pos, pos);
} else if ($(this).get(0).createTextRange) {
var range = $(this).get(0).createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
}(jQuery);


Source: Tips4all

12 comments:

  1. I have two functions:

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


    The you can use setCaretToPos like this:

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

    ReplyDelete
  2. I wanted to do this too, but I wasn't happy with the non-jQuery solution...

    $.fn.selectRange = function(start, end) {
    return this.each(function() {
    if (this.setSelectionRange) {
    this.focus();
    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();
    }
    });
    };


    With this, you can do

    $('#elem').selectRange(3,5);


    JsFiddle Example

    ReplyDelete
  3. The solutions here are right except for the jQuery extension code.

    The extension function should iterate over each selected element and return this to support chaining. Here is the a correct 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;
    };

    ReplyDelete
  4. I found a solution that works for 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;
    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);
    }


    Now you can move the focus to end of any element by calling:

    $(element).focusEnd();

    ReplyDelete
  5. I'm using this: http://plugins.jquery.com/project/jCaret

    ReplyDelete
  6. In IE to move cursor on some position this code is enough:

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

    ReplyDelete
  7. This worked for me on Safari 5 on Mac OSX, jQuery 1.4:

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

    ReplyDelete
  8. Just remember to return false right after the function call if you're using the arrow keys since Chrome fricks the frack up otherwise.

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

    ReplyDelete
  9. I found this in github:

    https://github.com/DrPheltRight/jquery-caret

    ReplyDelete
  10. You can directly change the prototype if setSelectionRange does not exist.

    (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

    ReplyDelete
  11. Based on this question, the answer will not work perfectly for ie and opera when there is new line in the textarea.
    The answer explain how to adjust the selectionStart, selectionEnd before calling setSelectionRange.

    I have try the adjustOffset from the other question with the solution proposed by @AVProgrammer and it work.

    function adjustOffset(el, offset) {
    /* From http://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 http://stackoverflow.com/a/7180862/611741 */
    if(this.lengh == 0) return this;
    return $(this).setSelection(position, position);
    }

    $.fn.setSelection = function(selectionStart, selectionEnd) {
    /* From http://stackoverflow.com/a/7180862/611741
    modified to fit http://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 http://stackoverflow.com/a/7180862/611741 */
    this.setCursorPosition(this.val().length);
    }

    ReplyDelete
  12. Small modification to the code I found in bitbucket

    Code is now able to select/highlight with start/end points if given 2 positions.
    Tested and works fine in FF/Chrome/IE9/Opera.

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


    The code is listed below, only a few lines changed:

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

    ReplyDelete