How to get range of selected text of textarea in JavaScript - javascript

I am trying to retrieve/find the start point and end point of selection in textarea.
Here is my code which work fine in Mozilla and chrome, but it is not working in Internet Explorer 9:
<script type="txt/javascript">
function update(o) {
var t = o.value, s = getSelectionStart(o), e = getSelectionEnd(o);
alert("start: " + s + " End: " + e);
}
function getSelectionStart(o) {
if (o.createTextRange) {
var r = document.selection.createRange().duplicate()
rse = r.text.length;
r.moveEnd('character', o.value.length)
if (r.text == '')
return o.value.length
return o.value.lastIndexOf(r.text)
}
else
return o.selectionStart
}
function getSelectionEnd(o) {
if (o.createTextRange) {
var r = document.selection.createRang;
e().duplicate()
r.moveStart('character', -o.value.length)
return r.text.length
}
else
return o.selectionEnd
}
</script>
<textarea id ="text" rows=10 cols="50" onselect="update(this);"></textarea>
When I test this code in Mozilla and Chrome, it gives me correct answer, but when I run this code in Internet Explorer 9, it shows -1 for start and any value for end.
I want to just find out the start and end point/index of the selected text of the textarea. Actually, the above code works fine for a textbox in all browsers, but not with textarea.
How can I fix it?

Use the code below or check this fiddle
function getTextSelection(el) {
var start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
alert("start :" + start + " End :" + end);
}

While the original answer may have helped the OP in 2012, things have changed, and this has now become simpler, at least in modern browsers.
You can use the JavaScript selectionStart and selectionEnd attributes of the textarea.
Basic Usage
These are both standard attributes that will work in today's major browsers (Chrome, Safari, Firefox, Opera, Edge, and IE).
For example:
console.log(
document.getElementById("text").selectionStart,
document.getElementById("text").selectionEnd
)
will show both the start and end point of the selection in the textarea with the ID text.
Boundary Cases
If there is no item selected in the textarea, then both the start and end attributes will return the last position of the caret. If the textarea has not received focus yet, the attributes will both return a value of 0.
Changing the Selection
By setting these attributes to new values, you will adjust the active text selection. Thus, you can also use this to select text in a textarea.
Checking if a selection is in place
You can check if there is currently a selection by checking if the values are both different, i.e. if
document.getElementById("text").selectionStart != document.getElementById("text").selectionEnd
is true, then there is a text selection.
Demo
const textarea = document.getElementById("text");
const selStart = document.getElementById("selStart");
const selEnd = document.getElementById("selEnd");
// should maybe also look at other events, e.g. "keydown", "select", etc
textarea.addEventListener("mousemove", () => {
selStart.innerText = textarea.selectionStart;
selEnd.innerText = textarea.selectionEnd;
});
<textarea id="text">Some text here</textarea>
<p>Selection Start: <span id="selStart">0</span></p>
<p>Selection End: <span id="selEnd">0</span></p>
References
Live Demo (JSFiddle)
Documentation at MDN
Documentation at MSDN

Related

Get occurence between # and textarea current position

I'd like to get the word after # depending on the current writing position of a textarea. More precisely:
if the current cursor position is on any letter of #<user>, the answer should be <user>
if the current cursor position is on any other word, the answer should be empty ''
I'm struggling with this, but don't find any "nice" way to do it.
$('#hey').on('click', function() { alert(); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<textarea id="chat">hello #user it's me this is a long text, here is another #username cheers!</textarea>
<span id="hey">CLICK ME</span>
Having updated the code from the assumed duplicate Get current word on caret position, the result is as follows
function getCaretPosition(ctrl) {
var start, end;
if (ctrl.setSelectionRange) {
start = ctrl.selectionStart;
end = ctrl.selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
start = 0 - range.duplicate().moveStart('character', -100000);
end = start + range.text.length;
}
return {
start: start,
end: end
}
}
$("textarea").on("click keyup", function () {
var caret = getCaretPosition(this);
var endPos = this.value.indexOf(' ',caret.end);
if (endPos ==-1) endPos = this.value.length;
var result = /\S+$/.exec(this.value.slice(0, endPos));
var lastWord = result ? result[0] : null;
if (lastWord) lastWord = lastWord.replace(/['";:,.\/?\\-]$/, ''); // remove punctuation
$("#atID").html((lastWord && lastWord.indexOf("#") == 0)?lastWord:"");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<textarea>Follow me on twitter #mplungjan if you want</textarea><br/>
<span id="atID"></span>

Logic for returning caret position for changed input

I'll be thankful for feedback on how i should build a logic of returning caret position for modified input.
Case: we have an input, processed by JS to be formatted like x999y999z9999, where x,y,z - are dividers we define on case by case basis. We process and modify it as intended, but i seem to become lost in logic for returning user's caret position in context of those x,y&z of variable length. I'm even kinda inclined to build a whole complex of if\else in response to those length fluctuations, but there probably is a simpler solution, which i'm missing.
Thanks in advance!
Code example: https://jsfiddle.net/zktva4kc/
function doGetCaretPosition (field) {
if (!!field){
var CaretPos = 0;
// IE Support
if (document.selection) {
field.focus ();
var Sel = document.selection.createRange ();
Sel.moveStart ('character', -field.value.length);
CaretPos = Sel.text.length;
}
// Firefox support
else if (field.selectionStart || field.selectionStart == '0')
CaretPos = field.selectionStart;
return (CaretPos);
}else{
console.log("No such field exist here for function initiation.");
}
}
function setCaretPosition(field, pos)
{
if (!!field){
if(field.setSelectionRange)
{
field.focus();
field.setSelectionRange(pos,pos);
}
else if (field.createTextRange) {
var range = field.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}else{
console.log("No such field exist here for function initiation.");
}
}
function formatItDown(field, format) {
if (!!field){
field.oninput = function () {
var position=doGetCaretPosition(field);
var sInput=this.value;
var input = this.value;
input = input.replace(/[^\d]/gi, "");
var first = input.substr(0, 3);
var second = input.substr(3, 3);
var third = input.substr(6, 4);
if (input.length > 3) {
first = format[0] + first + format[1];
}
if (input.length > 6) {
second = second + format[2];
}
formatted = first + second + third;
//x012y456z8901
/* this here is the problem area when we use some complex formats
if ((formatted[3]!=sInput[3])&&(position>3)&&(position<6)){
position=position+1;
}else if ((formatted[7]!=sInput[7])&&(position>7)){
position=position+1;
}*/
this.value = formatted;
setCaretPosition(field, position);
}
}else{
console.log("No such field exist here for function initiation.");
}
}
formatItDown(document.getElementById('exampleInput'), ["--","==","__"]);
<input id='exampleInput'>
Why not quickly google for something well coded and bug free ?
Here is a good plugin I used once
https://github.com/acdvorak/jquery.caret

add value to textarea using jquery (simple texteditor)

im trying to make a simple texteditor. its work fine, but i want to add the text on the selected text at textarea.
in my code, when i click bold button it will show [b][/b] with cursor focused inside b tags, and when i click the button again, it will show inside b tags again. (this is what i want dont mind this code)
function typeInTextarea(el, newText)
{
var start = el.prop("selectionStart")
var end = el.prop("selectionEnd")
var text = el.val()
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.val(before + newText + after)
el[0].selectionStart = el[0].selectionEnd = start - 4 + newText.length
el.focus()
return false
}
$("#button-bold").on("click", function()
{
typeInTextarea($("#textareapost"), "[b][/b]")
return false
});
what im looking right now is, when I select "abcd" then i click the button, it will show [b]abcd[/b], i success with this code:
function typeInTextarea(el, newText)
{
var start = el.prop("selectionStart")
var end = el.prop("selectionEnd")
var text = el.val()
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.val(before + newText.substring(0,3) + after + newText.substring(3,7)) //i making change in this one
el[0].selectionStart = el[0].selectionEnd = newText.length + end
el.focus()
return false
}
but when i didnt select the "abcd" text, it will show [b]abcd[/b][b][abcd/b]. just like copying the value of the text.
what im asking is, how to add value on selected text (not replace it)
and making if function when text is selected will add [b]abcd[/b] else will add [b] [/b] while the [b]abcd[/b] still there.
basicly it will be like stackoverflow editor, but without live view. thanks for advance, hope I found the answer. been looking this for a weeks.
found 1 answer
JQUERY Set Textarea Cursor to End of Text
and modify to
var b = { pos: 3, txt: function(s){ return '[b]' + s +'[/b]'; }}
$.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();
}
});
};
$('#this-b').on('click',function(e){
var cc = $('#this-txta')[0].selectionStart;
var str = $('#this-txta').val();
var en = $('#this-txta')[0].selectionEnd;
var sstr = str.substring(cc,en);
$('#this-txta').val( str.substring(0, cc) + b.txt(sstr) + str.substring(en) ).focus();
var pst = cc + b.pos
var pen = en + b.pos
$('#this-txta').selectRange(pst,pen);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="this-b"><b>B</b></button>
<br/>
<textarea id="this-txta">xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</textarea>
Sceditor has similar functionality and is open source.
Check how it was done here
http://www.sceditor.com/

Select portion of input/string instead of all of it

I have an input that holds a date value like so
03/15/2012
I am trying to select only portions of the the value instead of the whole thing. For instance if I click the spot before 2 in 2012 the year 2012 will be selected not the whole date (same for is true for months and day).
This is the code I am working with now
html:
<input class = "date-container" />
javascript/jquery:
$('.date-container').on('select', function (e)
e.preventDefault()
this.onselectstart = function () { return false; };
})
$('.date-container').on('focus', function ()
{
if (document.selection) {
this.focus();
var Sel = document.selection.createRange();
Sel.moveStart('character', -this.value.length);
CaretPos = Sel.text.length;
}
// Firefox support
else if (this.selectionStart || this.selectionStart == '0')
switch (this.selectionStart) {
case 0:
case 1:
this.selectionStart = 0;
this.selectionEnd = 1;
break;
}
}
I have tried a couple things so far. The code above is attempting to prevent the normal select action then based on where the focus is, select a portion of the string(I only have the switch statement options for the month portion, but if it worked I would do the same for day and year). This may be the wrong way to go about it.
Things to note:
By select I mean highlight.
I do not want to use plugins.
This code will select the portion of the date that is clicked on:
$(".date-container").click(function() {
var val = $(this).val();
var sel = this.selectionStart;
var firstSep = val.indexOf("/"), secondSep;
if (firstSep != -1) {
secondSep = val.indexOf("/", firstSep + 1);
if (secondSep != -1) {
if (sel < firstSep) {
this.setSelectionRange(0, firstSep);
} else if (sel < secondSep) {
this.setSelectionRange(firstSep + 1, secondSep);
} else {
this.setSelectionRange(secondSep + 1, val.length);
}
}
}
});​
You can see it work here: http://jsfiddle.net/jfriend00/QV4VT/

Find out the 'line' (row) number of the cursor in a textarea

I would like to find out and keep track of the 'line number' (rows) of the cursor in a textarea. (The 'bigger picture' is to parse the text on the line every time a new line is created/modified/selected, if of course the text was not pasted in. This saves parsing the whole text un-necessarily at set intervals.)
There are a couple of posts on StackOverflow however none of them specifically answer my question, most questions are for cursor position in pixels or displaying lines numbers besides the textarea.
My attempt is below, it works fine when starting at line 1 and not leaving the textarea. It fails when clicking out of the textarea and back onto it on a different line. It also fails when pasting text into it because the starting line is not 1.
My JavaScript knowledge is pretty limited.
<html>
<head>
<title>DEVBug</title>
<script type="text/javascript">
var total_lines = 1; // total lines
var current_line = 1; // current line
var old_line_count;
// main editor function
function code(e) {
// declare some needed vars
var keypress_code = e.keyCode; // key press
var editor = document.getElementById('editor'); // the editor textarea
var source_code = editor.value; // contents of the editor
// work out how many lines we have used in total
var lines = source_code.split("\n");
var total_lines = lines.length;
// do stuff on key presses
if (keypress_code == '13') { // Enter
current_line += 1;
} else if (keypress_code == '8') { // Backspace
if (old_line_count > total_lines) { current_line -= 1; }
} else if (keypress_code == '38') { // Up
if (total_lines > 1 && current_line > 1) { current_line -= 1; }
} else if (keypress_code == '40') { // Down
if (total_lines > 1 && current_line < total_lines) { current_line += 1; }
} else {
//document.getElementById('keycodes').innerHTML += keypress_code;
}
// for some reason chrome doesn't enter a newline char on enter
// you have to press enter and then an additional key for \n to appear
// making the total_lines counter lag.
if (total_lines < current_line) { total_lines += 1 };
// putput the data
document.getElementById('total_lines').innerHTML = "Total lines: " + total_lines;
document.getElementById('current_line').innerHTML = "Current line: " + current_line;
// save the old line count for comparison on next run
old_line_count = total_lines;
}
</script>
</head>
<body>
<textarea id="editor" rows="30" cols="100" value="" onkeydown="code(event)"></textarea>
<div id="total_lines"></div>
<div id="current_line"></div>
</body>
</html>
You would want to use selectionStart to do this.
<textarea onkeyup="getLineNumber(this, document.getElementById('lineNo'));" onmouseup="this.onkeyup();"></textarea>
<div id="lineNo"></div>
<script>
function getLineNumber(textarea, indicator) {
indicator.innerHTML = textarea.value.substr(0, textarea.selectionStart).split("\n").length;
}
</script>
This works when you change the cursor position using the mouse as well.
This is tough because of word wrap. It's a very easy thing to count the number of line breaks present, but what happens when the new row is because of word wrap? To solve this problem, it's useful to create a mirror (credit: github.com/jevin). Here's the idea:
Create a mirror of the textarea
Send the content from the beginning of the textarea to the cursor to the mirror
Use the height of the mirror to extract the current row
On JSFiddle
jQuery.fn.trackRows = function() {
return this.each(function() {
var ininitalHeight, currentRow, firstIteration = true;
var createMirror = function(textarea) {
jQuery(textarea).after('<div class="autogrow-textarea-mirror"></div>');
return jQuery(textarea).next('.autogrow-textarea-mirror')[0];
}
var sendContentToMirror = function (textarea) {
mirror.innerHTML = String(textarea.value.substring(0,textarea.selectionStart-1)).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br />') + '.<br/>.';
calculateRowNumber();
}
var growTextarea = function () {
sendContentToMirror(this);
}
var calculateRowNumber = function () {
if(firstIteration){
ininitalHeight = $(mirror).height();
currentHeight = ininitalHeight;
firstIteration = false;
} else {
currentHeight = $(mirror).height();
}
// Assume that textarea.rows = 2 initially
currentRow = currentHeight/(ininitalHeight/2) - 1;
//remove tracker in production
$('.tracker').html('Current row: ' + currentRow);
}
// Create a mirror
var mirror = createMirror(this);
// Style the mirror
mirror.style.display = 'none';
mirror.style.wordWrap = 'break-word';
mirror.style.whiteSpace = 'normal';
mirror.style.padding = jQuery(this).css('padding');
mirror.style.width = jQuery(this).css('width');
mirror.style.fontFamily = jQuery(this).css('font-family');
mirror.style.fontSize = jQuery(this).css('font-size');
mirror.style.lineHeight = jQuery(this).css('line-height');
// Style the textarea
this.style.overflow = "hidden";
this.style.minHeight = this.rows+"em";
var ininitalHeight = $(mirror).height();
// Bind the textarea's event
this.onkeyup = growTextarea;
// Fire the event for text already present
// sendContentToMirror(this);
});
};
$(function(){
$('textarea').trackRows();
});
This worked for me:
function getLineNumber(textarea) {
return textarea.value.substr(0, textarea.selectionStart) // get the substring of the textarea's value up to the cursor position
.split("\n") // split on explicit line breaks
.map((line) => 1 + Math.floor(line.length / textarea.cols)) // count the number of line wraps for each split and add 1 for the explicit line break
.reduce((a, b) => a + b, 0); // add all of these together
};
Inspired by colab's answer as a starting point, this includes the number of word wraps without having to introduce a mirror (as in bradbarbin's answer).
The trick is simply counting how many times the number of columns textarea.cols can divide the length of each segment between explicit line breaks \n.
Note: this starts counting at 1.

Categories