jcnnghm
jcnnghm

Reputation: 7432

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

Upvotes: 450

Views: 371808

Answers (16)

Tanya Koval&#39;
Tanya Koval&#39;

Reputation: 1

As for me here the most easily way to add text(Tab -> \t) in textarea by cursor position and save the focus on 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);
    }
});

Upvotes: 0

AVProgrammer
AVProgrammer

Reputation: 1350

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

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

$(element).focusEnd();

Or you specify the position.

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

Upvotes: 24

FGZ
FGZ

Reputation: 31

I had to get this working for contenteditable elements and jQuery and tought someone might want it ready to use:

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

Usage $(selector).getCaret() returns the number offset and $(selector).setCaret(num) establishes the offeset and sets focus on element.

Also a small tip, if you run $(selector).setCaret(num) from console it will return the console.log but you won't visualize the focus since it is established at the console window.

Bests ;D

Upvotes: 3

tofirius
tofirius

Reputation: 173

I do realize that this is a very old post, but I thought that I should offer perhaps a simpler solution to update it using only 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;
}

Usage for using ctrl-enter to add a new line (like 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);
    }
});

I am open to critique. Thank you.

UPDATE: This solution does not allow normal copy and paste functionality to work (i.e. ctrl-c, ctrl-v), so I will have to edit this in the future to make sure that part works again. If you have an idea how to do that, please comment here, and I will be happy to test it out. Thanks.

Upvotes: 9

Christian C. Salvad&#243;
Christian C. Salvad&#243;

Reputation: 827168

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

Then you can use setCaretToPos like this:

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

Live example with both a textarea and an input, showing use from 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>

As of 2016, tested and working on Chrome, Firefox, IE11, even IE8 (see that last here; Stack Snippets don't support IE8).

Upvotes: 264

mpen
mpen

Reputation: 282775

Here's a jQuery solution:

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

With this, you can do

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

Upvotes: 301

Hung Tran
Hung Tran

Reputation: 141

This works for me in chrome

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

Apparently you need a delay of a microsecond or more, because usually a user focusses on the text field by clicking at some position in the text field (or by hitting tab) which you want to override, so you have to wait till the position is set by the user click and then change it.

Upvotes: 6

Steven Whitby
Steven Whitby

Reputation: 87

Set the focus before you have inserted the text into the textarea thus?

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

Upvotes: 7

twig
twig

Reputation: 4184

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)

Upvotes: 4

Ghislain Hivon
Ghislain Hivon

Reputation: 131

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

Upvotes: 4

Anoop
Anoop

Reputation: 23208

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

Upvotes: 1

erich
erich

Reputation: 41

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

Upvotes: 4

HRJ
HRJ

Reputation: 17747

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

Upvotes: 37

BobFromBris
BobFromBris

Reputation: 261

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

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

Upvotes: 13

Ben Noland
Ben Noland

Reputation: 34938

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

Upvotes: 7

Kirilloid
Kirilloid

Reputation:

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

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

Upvotes: 7

Related Questions