184

I have a page with a lot of textboxes. When someone clicks a link, i want a word or two to be inserted where the cursor is, or appended to the textbox which has the focus.

For example, if the cursor/focus is on a textbox saying 'apple' and he clicks a link saying '[email]', then i want the textbox to say, 'apple bob@example.com'.

How can I do this? Is this even possible, since what if the focus is on a radio/dropdown/non textbox element? Can the last focused on textbox be remembered?

Ali
  • 261,656
  • 265
  • 575
  • 769
  • I assume it's possible because it's the basis of WYSISYG editors, how to do it, I don't know. – Ian Elliott Jun 30 '09 at 14:50
  • possible duplicate of [How do I insert a character at the caret with javascript?](http://stackoverflow.com/questions/54147/how-do-i-insert-a-character-at-the-caret-with-javascript) – John Jun 14 '12 at 13:32
  • Thank you for asking this question... now I can insert "[version]" at the cursor with my Chrome extension! – bb216b3acfd8f72cbc8f899d4d6963 Jul 28 '16 at 19:35
  • If you're looking for a simple module with undo support, try [insert-text-textarea](https://github.com/bfred-it/insert-text-textarea). If you need IE8+ support, try the [insert-text-at-cursor](https://www.npmjs.com/package/insert-text-at-cursor) package. – fregante Mar 29 '19 at 01:51

13 Answers13

251

Use this, from here:

function insertAtCaret(areaId, text) {
  var txtarea = document.getElementById(areaId);
  if (!txtarea) {
    return;
  }

  var scrollPos = txtarea.scrollTop;
  var strPos = 0;
  var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ?
    "ff" : (document.selection ? "ie" : false));
  if (br == "ie") {
    txtarea.focus();
    var range = document.selection.createRange();
    range.moveStart('character', -txtarea.value.length);
    strPos = range.text.length;
  } else if (br == "ff") {
    strPos = txtarea.selectionStart;
  }

  var front = (txtarea.value).substring(0, strPos);
  var back = (txtarea.value).substring(strPos, txtarea.value.length);
  txtarea.value = front + text + back;
  strPos = strPos + text.length;
  if (br == "ie") {
    txtarea.focus();
    var ieRange = document.selection.createRange();
    ieRange.moveStart('character', -txtarea.value.length);
    ieRange.moveStart('character', strPos);
    ieRange.moveEnd('character', 0);
    ieRange.select();
  } else if (br == "ff") {
    txtarea.selectionStart = strPos;
    txtarea.selectionEnd = strPos;
    txtarea.focus();
  }

  txtarea.scrollTop = scrollPos;
}
<textarea id="textareaid"></textarea>
<a href="#" onclick="insertAtCaret('textareaid', 'text to insert');return false;">Click Here to Insert</a>
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
George Claghorn
  • 26,261
  • 3
  • 48
  • 48
  • 7
    This worked great, but it doesn't handle replacing selected text as all text editors do. This may not be needed for the poster's original question, but if you need it, you can simply replace 'strPos' with 'txtArea.selectionEnd'. My answer below shows this, along with removing browser detection code, if you don't need to support older versions of IE. – Jeffrey Harmon Feb 05 '15 at 20:37
  • Is there a way to select all elements with `areaId`? – bb216b3acfd8f72cbc8f899d4d6963 Jul 28 '16 at 19:09
  • 1
    You can achieve the auto delete selected text by inserting a function like this. `function deleteTxt() { var ele = document.getElementById('yourTextarea'); var text = ele.value; text = text.slice(0, ele.selectionStart) + text.slice(ele.selectionEnd); ele.value = text; return text; }` – NSTuttle Sep 28 '16 at 20:41
  • How to insert text at mouse position? – Thamarai T May 14 '19 at 05:22
  • Youre the best! – Ecko Santoso May 16 '19 at 17:58
  • Borrowing NSTuttle's and adding insert of text arg: `function insertTxt(inId, inTxt) { var txt=inTxt || ""; var ele = document.getElementById(inId); if(!ele) return; var text = ele.value; text = text.slice(0, ele.selectionStart) + txt + text.slice(ele.selectionEnd); ele.value = text; return text; } ` – gordon May 21 '20 at 17:23
  • I know this is old, but I've run into a situation. I want to be able to add quotation marks to the textarea, but that breaks this code. How would I allow quotes through in the `text to insert` parameter? – Anna Jun 13 '21 at 16:55
  • Nevermind; I'm dumb. I just used `"` instead of a physical quote. – Anna Jun 13 '21 at 16:57
166

Maybe a shorter version, would be easier to understand?

$('#btn').click(function() {
    const element = $('#txt');
    const caretPos = element[0].selectionStart;
    const textAreaTxt = element.val();
    const txtToAdd = "stuff";

    element.val(textAreaTxt.substring(0, caretPos) + txtToAdd + textAreaTxt.substring(caretPos) );
});
<script src="https://code.jquery.com/jquery-3.6.3.min.js" crossorigin="anonymous"></script>
<textarea id="txt" rows="15" cols="70">There is some text here.</textarea>
<input type="button" id="btn" value="OK" />

I wrote this in response to How to add a text to a textbox from the current position of the pointer with jquery?

unknown6656
  • 2,765
  • 2
  • 36
  • 52
quik_silv
  • 2,397
  • 2
  • 15
  • 21
  • 13
    You can also restore caret position after changing value with : `document.getElementById("txt").selectionStart = caretPos + txtToAdd.length`. – Bludwarf Aug 14 '15 at 10:45
  • 1
    worked for me, thanks. FYI the "selectionStart" property doesn't exist through jquery selector $("#"). You need to go through document.getElementById() – Lego Dec 13 '15 at 12:25
  • 1
    http://jsfiddle.net/NaHTw/802/ is your solution with a little extra added to replace the selected area with what's pasted – patrick Dec 20 '15 at 01:07
  • 3
    Well, if you're going to go that far removing the jQuery, you might as well [remove it all](http://jsfiddle.net/NaHTw/902/). ;^) – ruffin Apr 19 '16 at 21:39
  • 1
    This worked great. Added $txt.focus() at the end to refocus the field. – ginna Feb 22 '17 at 18:25
  • 10
    Just to help, here's the complete solution with caret position restore plus replace selected area with pasted content: http://jsfiddle.net/NaHTw/1028/ – AlfaTeK May 04 '17 at 17:40
  • @AlfaTeK & ruffin both your solutions are near perfect, the only issue is that when the textareas become scrollable, they scroll to the bottom after clicking the button. Move the .focus() method to AFTER the selectionStart and selectionEnd declarations and you've got a perfect solution :) – blueprintchris Jun 07 '17 at 10:16
  • @Bludwarf thank you for your explanation, actually an [older answer in here](https://stackoverflow.com/a/4384173/6225838) was doing that as well (and also resetting `selectionEnd`). I guess it was "too big" to be noticed? – CPHPython Jul 10 '18 at 14:57
43

The approved answer from George Claghorn worked great for simply inserting text at the cursor position. If the user had selected text though, and you want that text to be replaced (the default experience with most text), you need to make a small change when setting the 'back' variable.

Also, if you don't need to support older versions of IE, modern versions support textarea.selectionStart, so you can take out all of the browser detection, and IE-specific code.

Here is a simplified version that works for Chrome and IE11 at least, and handles replacing selected text.

function insertAtCaret(areaId, text) {
    var txtarea = document.getElementById(areaId);
    var scrollPos = txtarea.scrollTop;
    var caretPos = txtarea.selectionStart;

    var front = (txtarea.value).substring(0, caretPos);
    var back = (txtarea.value).substring(txtarea.selectionEnd, txtarea.value.length);
    txtarea.value = front + text + back;
    caretPos = caretPos + text.length;
    txtarea.selectionStart = caretPos;
    txtarea.selectionEnd = caretPos;
    txtarea.focus();
    txtarea.scrollTop = scrollPos;
}
Jeffrey Harmon
  • 2,268
  • 23
  • 18
  • This is good, but it doesn't take the element's `maxlength` into account, so the resulting value after the insert could be longer than the maximum. – mbomb007 May 03 '19 at 16:07
24

The code above didn't work for me in IE. Here's some code based on this answer.

I took out the getElementById so I could reference the element in a different way.

function insertAtCaret(element, text) {
  if (document.selection) {
    element.focus();
    var sel = document.selection.createRange();
    sel.text = text;
    element.focus();
  } else if (element.selectionStart || element.selectionStart === 0) {
    var startPos = element.selectionStart;
    var endPos = element.selectionEnd;
    var scrollTop = element.scrollTop;
    element.value = element.value.substring(0, startPos) +
      text + element.value.substring(endPos, element.value.length);
    element.focus();
    element.selectionStart = startPos + text.length;
    element.selectionEnd = startPos + text.length;
    element.scrollTop = scrollTop;
  } else {
    element.value += text;
    element.focus();
  }
}
input{width:100px}
label{display:block;margin:10px 0}
<label for="in2copy">Copy text from: <input id="in2copy" type="text" value="x"></label>
<label for="in2ins">Element to insert: <input id="in2ins" type="text" value="1,2,3" autofocus></label>
<button onclick="insertAtCaret(document.getElementById('in2ins'),document.getElementById('in2copy').value)">Insert</button>

EDIT: Added a running snippet, jQuery is not being used.

CPHPython
  • 12,379
  • 5
  • 59
  • 71
Collin Anderson
  • 14,787
  • 6
  • 68
  • 57
10

using @quick_sliv answer:

function insertAtCaret(el, text) {
    var caretPos = el.selectionStart;
    var textAreaTxt = el.value;
    el.value = textAreaTxt.substring(0, caretPos) + text + textAreaTxt.substring(caretPos);
};
math2001
  • 4,167
  • 24
  • 35
4

How to insert some Text to current cursor position of a TextBox through JQuery and JavaScript

Process

  1. Find the Current Cursor Position
  2. Get the Text to be Copied
  3. Set the Text Over there
  4. Update the Cursor position

Here I have 2 TextBoxes and a Button. I have to Click on a certain position on a textbox and then click on the button to paste the text from the other textbox to the the position of the previous textbox.

Main issue here is that getting the current cursor position where we will paste the text.

//Textbox on which to be pasted
<input type="text" id="txtOnWhichToBePasted" />

//Textbox from where to be pasted
<input type="text" id="txtFromWhichToBePasted" />


//Button on which click the text to be pasted
<input type="button" id="btnInsert" value="Insert"/>


<script type="text/javascript">

$(document).ready(function () {
    $('#btnInsert').bind('click', function () {
            var TextToBePasted = $('#txtFromWhichToBePasted').value;
            var ControlOnWhichToBePasted = $('#txtOnWhichToBePasted');

            //Paste the Text
            PasteTag(ControlOnWhichToBePasted, TextToBePasted);
        });
    });

//Function Pasting The Text
function PasteTag(ControlOnWhichToBePasted,TextToBePasted) {
    //Get the position where to be paste

    var CaretPos = 0;
    // IE Support
    if (document.selection) {

        ControlOnWhichToBePasted.focus();
        var Sel = document.selection.createRange();

        Sel.moveStart('character', -ctrl.value.length);

        CaretPos = Sel.text.length;
    }
    // Firefox support
    else if (ControlOnWhichToBePasted.selectionStart || ControlOnWhichToBePasted.selectionStart == '0')
        CaretPos = ControlOnWhichToBePasted.selectionStart;

    //paste the text
    var WholeString = ControlOnWhichToBePasted.value;
    var txt1 = WholeString.substring(0, CaretPos);
    var txt2 = WholeString.substring(CaretPos, WholeString.length);
    WholeString = txt1 + TextToBePasted + txt2;
    var CaretPos = txt1.length + TextToBePasted.length;
    ControlOnWhichToBePasted.value = WholeString;

    //update The cursor position 
    setCaretPosition(ControlOnWhichToBePasted, CaretPos);
}

function setCaretPosition(ControlOnWhichToBePasted, pos) {

    if (ControlOnWhichToBePasted.setSelectionRange) {
        ControlOnWhichToBePasted.focus();
        ControlOnWhichToBePasted.setSelectionRange(pos, pos);
    }
    else if (ControlOnWhichToBePasted.createTextRange) {
        var range = ControlOnWhichToBePasted.createTextRange();
        range.collapse(true);
        range.moveEnd('character', pos);
        range.moveStart('character', pos);
        range.select();
    }
}

</script>
Chris Peters
  • 17,918
  • 6
  • 49
  • 65
3

Adding text to current cursor position involves two steps:

  1. Adding the text at the current cursor position
  2. Updating the current cursor position

Demo: https://codepen.io/anon/pen/qZXmgN

Tested in Chrome 48, Firefox 45, IE 11 and Edge 25

JS:

function addTextAtCaret(textAreaId, text) {
    var textArea = document.getElementById(textAreaId);
    var cursorPosition = textArea.selectionStart;
    addTextAtCursorPosition(textArea, cursorPosition, text);
    updateCursorPosition(cursorPosition, text, textArea);
}
function addTextAtCursorPosition(textArea, cursorPosition, text) {
    var front = (textArea.value).substring(0, cursorPosition);
    var back = (textArea.value).substring(cursorPosition, textArea.value.length);
    textArea.value = front + text + back;
}
function updateCursorPosition(cursorPosition, text, textArea) {
    cursorPosition = cursorPosition + text.length;
    textArea.selectionStart = cursorPosition;
    textArea.selectionEnd = cursorPosition;
    textArea.focus();    
}

HTML:

<div>
    <button type="button" onclick="addTextAtCaret('textArea','Apple')">Insert Apple!</button>
    <button type="button" onclick="addTextAtCaret('textArea','Mango')">Insert Mango!</button>
    <button type="button" onclick="addTextAtCaret('textArea','Orange')">Insert Orange!</button>
</div>
<textarea id="textArea" rows="20" cols="50"></textarea>
Razan Paul
  • 13,618
  • 3
  • 69
  • 61
2

I think you could use the following JavaScript to track the last-focused textbox:

<script>
var holdFocus;

function updateFocus(x)
{
    holdFocus = x;
}

function appendTextToLastFocus(text)
{
    holdFocus.value += text;
}
</script>

Usage:

<input type="textbox" onfocus="updateFocus(this)" />
<a href="#" onclick="appendTextToLastFocus('textToAppend')" />

A previous solution (props to gclaghorn) uses textarea and calculates the position of the cursor too, so it may be better for what you want. On the other hand, this one would be more lightweight, if that's what you're looking for.

DreadPirateShawn
  • 8,164
  • 4
  • 49
  • 71
  • Good idea. I used jquery so each textbox which had a certain class had this event applied to them rather than having to put "onfocus=..." in the html of each textbox. But good approach :) – Ali Jun 30 '09 at 15:04
2

This question's answer was posted so long ago and I stumbled upon it via a Google search. HTML5 provides the HTMLInputElement API that includes the setRangeText() method, which replaces a range of text in an <input> or <textarea> element with a new string:

element.setRangeText('abc');

The above would replace the selection made inside element with abc. You can also specify which part of the input value to replace:

element.setRangeText('abc', 3, 5);

The above would replace the 4th till 6th characters of the input value with abc. You can also specify how the selection should be set after the text has been replaced by providing one of the following strings as the 4th parameter:

  • 'preserve' attempts to preserve the selection. This is the default.
  • 'select' selects the newly inserted text.
  • 'start' moves the selection to just before the inserted text.
  • 'end' moves the selection to just after the inserted text.

Browser compatibility

The MDN page for setRangeText doesn't provide browser compatibility data, but I guess it'd be the same as HTMLInputElement.setSelectionRange(), which is basically all modern browsers, IE 9 and above, Edge 12 and above.

A. Genedy
  • 578
  • 4
  • 11
  • 1
    The "setRangeText" mozilla page you linked has a "browser compatibility" section at the bottom now: IE isn't supported but every other modern browser (Chrome, FF, Edge,...) is. To fully answer the op's question, here's some additional info: You can use `element.selectionStart` and `element.selectionEnd` to find the cursor's position. If start!=end, then the user marked some text, otherwise it's simply the cursor's position. "0" means at the very start of the text element, if there are line breaks (e.g. in a text area), it just continues counting. – Neph Jun 24 '21 at 11:06
  • Some more info now that I'm done testing everything: I recommend using `end` for the most "natural" feel: The cursor is put at the end of the inserted text, which makes it possible to insert multiple things in a row, without having to move the cursor. It also allows replacing already existing text and afterwards, unlike `preserve`, doesn't select the "from to" that was selected before but instead puts the cursor at the end of the inserted text again. This is how Windows does it (not 100% sure about macOS or Linux) and I'm really surprised it's not the default setting. – Neph Jun 30 '21 at 13:34
1

The accepted answer didn't work for me on Internet Explorer 9. I checked it and the browser detection was not working properly, it detected ff (firefox) when i was at Internet Explorer.

I just did this change:

if ($.browser.msie) 

Instead of:

if (br == "ie") { 

The resulting code is this one:

function insertAtCaret(areaId,text) {
    var txtarea = document.getElementById(areaId);
    var scrollPos = txtarea.scrollTop;
    var strPos = 0;
    var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ? 
        "ff" : (document.selection ? "ie" : false ) );

    if ($.browser.msie) { 
        txtarea.focus();
        var range = document.selection.createRange();
        range.moveStart ('character', -txtarea.value.length);
        strPos = range.text.length;
    }
    else if (br == "ff") strPos = txtarea.selectionStart;

    var front = (txtarea.value).substring(0,strPos);  
    var back = (txtarea.value).substring(strPos,txtarea.value.length); 
    txtarea.value=front+text+back;
    strPos = strPos + text.length;
    if (br == "ie") { 
        txtarea.focus();
        var range = document.selection.createRange();
        range.moveStart ('character', -txtarea.value.length);
        range.moveStart ('character', strPos);
        range.moveEnd ('character', 0);
        range.select();
    }
    else if (br == "ff") {
        txtarea.selectionStart = strPos;
        txtarea.selectionEnd = strPos;
        txtarea.focus();
    }
    txtarea.scrollTop = scrollPos;
}
Kemal Fadillah
  • 9,760
  • 3
  • 45
  • 63
Alvaro
  • 40,778
  • 30
  • 164
  • 336
1

This jQuery plugin gives you a pre-made way of selection/caret manipulation.

Julian
  • 33,915
  • 22
  • 119
  • 174
Chris S
  • 64,770
  • 52
  • 221
  • 239
0

you can only focus required textbox an insert the text there. there is no way to find out where focus is AFAIK (maybe interating over all DOM nodes?).

check this stackoverflow - it has a solution for you: How do I find out which DOM element has the focus?

Community
  • 1
  • 1
dusoft
  • 11,289
  • 5
  • 38
  • 44
0

Content Editable, HTML or any other DOM element Selections

If you are trying to insert at caret on a <div contenteditable="true">, this becomes much more difficult, especially if there are children within the editable container.

I have had really great luck using the Rangy library:

It has a ton of great features such as:

  • Save Position or Selection
  • Then later, Restore the Position or Selection
  • Get selection HTML or Plaintext
  • Among many others

The online demo was not working last I checked, however the repo has working demos. To get started, simple download the Repo from Git or NPM, then open ./rangy/demos/index.html

It makes working with caret pos and text selection a breeze!

factorypolaris
  • 2,757
  • 12
  • 15