Bold a specific text inside a contenteditable div - javascript

I have this in my contenteditable div. Whenever I type #something, then type space, I would like to bold that word instantly in that div.
E.g.
This is my message. #lol. I would like to bold the hashtag.
Below is the code I have
<div id="message" name="message" contenteditable="true"></div>
$('#message').keyup(function(e){
var len = $(this).val().length;
if ($(this).val().substring(length - 1, 1) == '#') {
}
//detect space
if(e.keyCode == 32){
}
});
I am using jquery. How do i go about doing so?

Using contenteditable = "true" may be tricky, but this is a possible solution:
The text is bold when a word is preceded by #
Example: hello #world, this is a #sample
HTML:
<div id="divEditable" contenteditable="true"></div>
JavaScript: jsbin.com/zisote
We are going to split the code, but actually it is wrapped in an IIFE
/*** Cached private variables ***/
var _break = /<br\s?\/?>$/g,
_rxword = /(#[\w]+)$/gm,
_rxboldDown = /<\/b>$/gm,
_rxboldUp = /<\/b>(?!<br\s?\/?>)([\w]+)(?:<br\s?\/?>)?$/,
_rxline = /(<br\s?\/?>)+(?!<b>)(<\/b>$)+/g;
/*** Handles the event when a key is pressed ***/
$(document).on("keydown.editable", '.divEditable', function (e) {
//fixes firefox NodeContent which ends with <br>
var html = this.innerHTML.replace(_break, ""),
key = (e.which || e.keyCode),
dom = $(this);
//space key was pressed
if (key == 32 || key == 13) {
//finds the # followed by a word
if (_rxword.test(dom.text()))
dom.data("_newText", html.replace(_rxword, "<b>$1</b> "));
//finds the end of bold text
if (_rxboldDown.test(html))
dom.data("_newText", html + " ");
}
//prevents the bold NodeContent to be cached
dom.attr("contenteditable", false).attr("contenteditable", true);
});
/*** Handles the event when the key is released ***/
$(document).on("keyup.editable", '.divEditable', function (e) {
var dom = $(this),
newText = dom.data("_newText"),
innerHtml = this.innerHTML,
html;
//resets the NodeContent
if (!dom.text().length || innerHtml == '<br>') {
dom.empty();
return false;
}
//fixes firefox issue when text must follow with bold
if (!newText && _rxboldUp.test(innerHtml))
newText = innerHtml.replace(_rxboldUp, "$1</b>");
//fixes firefox issue when space key is rendered as <br>
if (!newText && _rxline.test(innerHtml)) html = innerHtml;
else if (newText && _rxline.test(newText)) html = newText;
if (html) newText = html.replace(_rxline, "$2$1");
if (newText && newText.length) {
dom.html(newText).removeData("_newText");
placeCaretAtEnd(this);
}
});
/*** Sets the caret position at end of NodeContent ***/
function placeCaretAtEnd (dom) {
var range, sel;
dom.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
range = document.createRange();
range.selectNodeContents(dom);
range.collapse(false);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
//this handles the text selection in IE
range = document.body.createTextRange();
range.moveToElementText(dom);
range.collapse(false);
range.select();
}
}
You can play with live code here: jsbin.com/zisote

Here is a sample. An editable div is not a good idea though. Try to change this. A textarea maybe is better...
http://jsfiddle.net/blackjim/h9dck/5/
function makeBold(match, p1, p2, p3, offset, string) {
return p1+'<b>' + p2 + '</b>'+p3;
};
$('#message').keyup(function (e) {
var $this = $(this);
//detect space
if (e.keyCode == 32) {
var innerHtml = $this.html();
innerHtml = innerHtml.replace(/(.*[\s|($nbsp;)])(#\w+)(.*)/g, makeBold);
if(innerHtml !== $this.html()){
$this.html(innerHtml)
.focus();
}
}
});

Related

How to shift cursor position in a text using javascript [duplicate]

Does anybody know how to move the keyboard caret in a textbox to a particular position?
For example, if a text-box (e.g. input element, not text-area) has 50 characters in it and I want to position the caret before character 20, how would I go about it?
This is in differentiation from this question: jQuery Set Cursor Position in Text Area , which requires jQuery.
Excerpted from Josh Stodola's Setting keyboard caret Position in a Textbox or TextArea with Javascript
A generic function that will allow you to insert the caret at any position of a textbox or textarea that you wish:
function setCaretPosition(elemId, caretPos) {
var elem = document.getElementById(elemId);
if(elem != null) {
if(elem.createTextRange) {
var range = elem.createTextRange();
range.move('character', caretPos);
range.select();
}
else {
if(elem.selectionStart) {
elem.focus();
elem.setSelectionRange(caretPos, caretPos);
}
else
elem.focus();
}
}
}
The first expected parameter is the ID of the element you wish to insert the keyboard caret on. If the element is unable to be found, nothing will happen (obviously). The second parameter is the caret positon index. Zero will put the keyboard caret at the beginning. If you pass a number larger than the number of characters in the elements value, it will put the keyboard caret at the end.
Tested on IE6 and up, Firefox 2, Opera 8, Netscape 9, SeaMonkey, and Safari. Unfortunately on Safari it does not work in combination with the onfocus event).
An example of using the above function to force the keyboard caret to jump to the end of all textareas on the page when they receive focus:
function addLoadEvent(func) {
if(typeof window.onload != 'function') {
window.onload = func;
}
else {
if(func) {
var oldLoad = window.onload;
window.onload = function() {
if(oldLoad)
oldLoad();
func();
}
}
}
}
// The setCaretPosition function belongs right here!
function setTextAreasOnFocus() {
/***
* This function will force the keyboard caret to be positioned
* at the end of all textareas when they receive focus.
*/
var textAreas = document.getElementsByTagName('textarea');
for(var i = 0; i < textAreas.length; i++) {
textAreas[i].onfocus = function() {
setCaretPosition(this.id, this.value.length);
}
}
textAreas = null;
}
addLoadEvent(setTextAreasOnFocus);
The link in the answer is broken, this one should work (all credits go to blog.vishalon.net):
http://snipplr.com/view/5144/getset-cursor-in-html-textarea/
In case the code gets lost again, here are the two main functions:
function doGetCaretPosition(ctrl)
{
var CaretPos = 0;
if (ctrl.selectionStart || ctrl.selectionStart == 0)
{// Standard.
CaretPos = ctrl.selectionStart;
}
else if (document.selection)
{// Legacy IE
ctrl.focus ();
var Sel = document.selection.createRange ();
Sel.moveStart ('character', -ctrl.value.length);
CaretPos = Sel.text.length;
}
return (CaretPos);
}
function setCaretPosition(ctrl,pos)
{
if (ctrl.setSelectionRange)
{
ctrl.focus();
ctrl.setSelectionRange(pos,pos);
}
else if (ctrl.createTextRange)
{
var range = ctrl.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
Since I actually really needed this solution, and the typical baseline solution (focus the input - then set the value equal to itself) doesn't work cross-browser, I spent some time tweaking and editing everything to get it working. Building upon #kd7's code here's what I've come up with.
Enjoy! Works in IE6+, Firefox, Chrome, Safari, Opera
Cross-browser caret positioning technique (example: moving the cursor to the END)
// ** USEAGE ** (returns a boolean true/false if it worked or not)
// Parameters ( Id_of_element, caretPosition_you_want)
setCaretPosition('IDHERE', 10); // example
The meat and potatoes is basically #kd7's setCaretPosition, with the biggest tweak being if (el.selectionStart || el.selectionStart === 0), in firefox the selectionStart is starting at 0, which in boolean of course is turning to False, so it was breaking there.
In chrome the biggest issue was that just giving it .focus() wasn't enough (it kept selecting ALL of the text!) Hence, we set the value of itself, to itself el.value = el.value; before calling our function, and now it has a grasp & position with the input to use selectionStart.
function setCaretPosition(elemId, caretPos) {
var el = document.getElementById(elemId);
el.value = el.value;
// ^ this is used to not only get "focus", but
// to make sure we don't have it everything -selected-
// (it causes an issue in chrome, and having it doesn't hurt any other browser)
if (el !== null) {
if (el.createTextRange) {
var range = el.createTextRange();
range.move('character', caretPos);
range.select();
return true;
}
else {
// (el.selectionStart === 0 added for Firefox bug)
if (el.selectionStart || el.selectionStart === 0) {
el.focus();
el.setSelectionRange(caretPos, caretPos);
return true;
}
else { // fail city, fortunately this never happens (as far as I've tested) :)
el.focus();
return false;
}
}
}
}
I found an easy way to fix this issue, tested in IE and Chrome:
function setCaret(elemId, caret)
{
var elem = document.getElementById(elemId);
elem.setSelectionRange(caret, caret);
}
Pass text box id and caret position to this function.
I've adjusted the answer of kd7 a little bit because elem.selectionStart will evaluate to false when the selectionStart is incidentally 0.
function setCaretPosition(elem, caretPos) {
var range;
if (elem.createTextRange) {
range = elem.createTextRange();
range.move('character', caretPos);
range.select();
} else {
elem.focus();
if (elem.selectionStart !== undefined) {
elem.setSelectionRange(caretPos, caretPos);
}
}
}
If you need to focus some textbox and your only problem is that the entire text gets highlighted whereas you want the caret to be at the end, then in that specific case, you can use this trick of setting the textbox value to itself after focus:
$("#myinputfield").focus().val($("#myinputfield").val());
function SetCaretEnd(tID) {
tID += "";
if (!tID.startsWith("#")) { tID = "#" + tID; }
$(tID).focus();
var t = $(tID).val();
if (t.length == 0) { return; }
$(tID).val("");
$(tID).val(t);
$(tID).scrollTop($(tID)[0].scrollHeight); }
I would fix the conditions like below:
function setCaretPosition(elemId, caretPos)
{
var elem = document.getElementById(elemId);
if (elem)
{
if (typeof elem.createTextRange != 'undefined')
{
var range = elem.createTextRange();
range.move('character', caretPos);
range.select();
}
else
{
if (typeof elem.selectionStart != 'undefined')
elem.selectionStart = caretPos;
elem.focus();
}
}
}
<!DOCTYPE html>
<html>
<head>
<title>set caret position</title>
<script type="application/javascript">
//<![CDATA[
window.onload = function ()
{
setCaret(document.getElementById('input1'), 13, 13)
}
function setCaret(el, st, end)
{
if (el.setSelectionRange)
{
el.focus();
el.setSelectionRange(st, end);
}
else
{
if (el.createTextRange)
{
range = el.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', st);
range.select();
}
}
}
//]]>
</script>
</head>
<body>
<textarea id="input1" name="input1" rows="10" cols="30">Happy kittens dancing</textarea>
<p> </p>
</body>
</html>
HTMLInputElement.setSelectionRange( selectionStart, selectionEnd );
// References
var e = document.getElementById( "helloworldinput" );
// Move caret to beginning on focus
e.addEventListener( "focus", function( event )
{
// References
var e = event.target;
// Action
e.setSelectionRange( 0, 0 ); // Doesn’t work for focus event
window.setTimeout( function()
{
e.setSelectionRange( 0, 0 ); // Works
//e.setSelectionRange( 1, 1 ); // Move caret to second position
//e.setSelectionRange( 1, 2 ); // Select second character
}, 0 );
}, false );
Browser compatibility (only for types: text, search, url, tel and password):
https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange#Specifications

How Facebook changes the Emoji from their symbols while chatting?

First Image - Initial Text when typing something in Facebook Chat Box
Second Image - The moment you hit space it converts to this!
I have seen in developer console it is not the input box at all, they are using span and all with background-image to do it but how to actually combine it completely, to avoid any clutter whatsoever. I am attaching a link of codepen of what I did when pressing Enter key. But not able to do for the Space Bar. Codepen Link Anything you guys can help. Thanks in advance. NOTE :- No external libraries and would prefer Javascript answer.
var emojiContainer = {
":)" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zeb/2/16/1f642.png",
"<3" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zed/2/16/2764.png"
};
var chatDetailText = document.getElementById('chatDetailText');
chatDetailText.addEventListener("keypress", function (e) {
console.log("Inside keypress event")
var textEntered = chatDetailText.value;
var keyPressed = e.which || e.keyCode;
console.log(emojiContainer[this.value])
if (keyPressed === 13){
console.log("inside keypress 13")
if(emojiContainer[this.value]){
console.log("inside value found of enter")
var emojiImg = document.createElement("img");
emojiImg.src = emojiContainer[this.value];
document.getElementById('enterPressed').appendChild(emojiImg);
document.getElementById('chatDetailText').value = '';
}else{
console.log("value not found of enter")
var divChatDetail = document.createElement('div');
/*chatDetailSeperator.className = "aClassName";*/ //To add class name for div
divChatDetail.innerHTML = this.value;
document.getElementById('enterPressed').appendChild(divChatDetail);
document.getElementById('chatDetailText').value = '';
}
}
}, false);
You can use HTML5 ContentEditable attribute for div.
here is just an example. Take care of cert position etc.
var emojiContainer = {
":)" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zeb/2/16/1f642.png",
"<3" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zed/2/16/2764.png"
};
var chatDetailText = document.getElementById('chatDetailText');
chatDetailText.addEventListener("keypress", function (e) {
console.log("Inside keypress event")
var textEntered = chatDetailText.innerHTML;
var keyPressed = e.which || e.keyCode;
console.log(keyPressed)
if (keyPressed === 32){
var last_word = textEntered.split(" ");
last_word = last_word[last_word.length-1];
console.log(last_word);
if(emojiContainer[last_word]){
console.log("inside value found of enter")
var emojiImg = "<img src='"+emojiContainer[last_word]+"' >";
textEntered = textEntered.replace(last_word, emojiImg)
chatDetailText.innerHTML = textEntered;
}
}
}, false);
<div id="enterPressed"></div>
<div contenteditable="true" id="chatDetailText" >edit this</div>
I got it done, thanks to Zeeshan for helping me with contenteditable. Do update if you have any improvisations.
var emojiContainer = {
":)" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zeb/2/16/1f642.png",
"<3" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zed/2/16/2764.png"
};
var chatDetailText = document.getElementById('chatDetailText');
chatDetailText.addEventListener("keydown", function (e) {
//to perform the action based on pressing space bar (32) or enter (13).
var keydown = e.which || e.keyCode;
//to get the pointer location and modify to place to the end if needed
var selectionInfo = getSelectionTextInfo(this);
//to get the complete text extered by the user.
var input = chatDetailText.innerHTML;
//to cover the cases in which user enters <3 and gets interpreted as &lt
var textEntered = decodeHtml(input);
//To split the text entered and to get the location of the emoji for conversion
var last_word = textEntered.split(/\s{1}/);
//After splitting contains the emoji and now can be accessed.
last_word = last_word[last_word.length-1];
//space bar is pressed and the smiley is just inserted
if (keydown === 32 && selectionInfo.atEnd){
//if the emoji is available in our database, it'll replace the same using the Facebook url which is currently used.
if(emojiContainer[last_word]){
var emojiImg = "<img src='"+emojiContainer[last_word]+"' >";
textEntered = textEntered.replace(last_word, emojiImg);
chatDetailText.innerHTML = textEntered;
elemIterate = document.getElementById('chatDetailText');//This is the element to move the caret to the end of
setEndOfContenteditable(elemIterate);
}
//Enter key is pressed after typing the emoji
}else if (keydown === 13) {
// To avoid extra line insertion in div.
e.preventDefault();
//if the emoji is available in our database, it'll replace the same using the Facebook url which is currently used.
if(emojiContainer[last_word]){
var emojiImg = document.createElement("img");
emojiImg.src = emojiContainer[last_word];
var spanChatElement = document.createElement("span");
var precedingChatContent = textEntered.split(/\s{1}/);
precedingChatContent.pop(); //To pop the last smiley found
if(precedingChatContent.length !=0){
precedingChatContent = precedingChatContent.join(" ");
spanChatElement.innerHTML = precedingChatContent;
document.getElementById('enterPressed').appendChild(spanChatElement);
}
document.getElementById('enterPressed').appendChild(emojiImg);
document.getElementById('chatDetailText').innerHTML = '';
}else{
//If no Smiley found, just the plain text it'll automatically display the text in a div
var divChatElement = document.createElement('div');
//chatDetailSeperator.className = "aClassName"; To add class name for div
divChatElement.innerHTML = textEntered;
document.getElementById('enterPressed').appendChild(divChatElement);
document.getElementById('chatDetailText').innerHTML = '';
}
}
}, false);
function decodeHtml(html) {
var textAreaElement = document.createElement("textarea");
textAreaElement.innerHTML = html;
return textAreaElement.value;
}
//To send the pointer to the end of the div.
function setEndOfContenteditable(contentEditableElement){
var range,selection;
if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange();//Create a range (a range is like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createTextRange();//Create a range (a range is like the selection but invisible)
range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
//To check if it is at the end.
function getSelectionTextInfo(contentEditableElement) {
var atEnd = false;
var selectionRange, testRange;
if (window.getSelection) {
var windowSelection = window.getSelection();
if (windowSelection.rangeCount) {
selectionRange = windowSelection.getRangeAt(0);
testRange = selectionRange.cloneRange();
testRange.selectNodeContents(contentEditableElement);
testRange.setStart(selectionRange.endContainer, selectionRange.endOffset);
atEnd = (testRange.toString() == "");
}
}else if (document.selection && document.selection.type != "Control") {
selectionRange = document.selection.createRange();
testRange = selectionRange.duplicate();
testRange.moveToElementText(contentEditableElement);
testRange.setEndPoint("StartToEnd", selectionRange);
atEnd = (testRange.text == "");
}
return { atEnd: atEnd };
}
You just need to change the line if (keyPressed === 13){ to if (keyPressed === 32){ in your codepen link. And to stop that from posting the comment, you just need to add another function for if (keypressed === 13).

Focus an input field when clicking on an element, except if some text within the container was highlighted

An input field #chatInput needs to be be focused when clicking on a container element #text EXCEPT if text inside that element was (highlighted via either double click or mouse selection)
// what I got so far which is incomplete
$('#text').on('click', function (e) {
$('#chatInput').focus();
});
Fiddle: https://jsfiddle.net/xhykmtwy/4/
You may want to consider the solution below, which checks if some text is selected in the text element after the click event:
$('#text').click(function () {
var container = this;
setTimeout(function () {
var selectedElement = null;
var selectedText = null;
if (window.getSelection) {
// For modern browsers
var selection = window.getSelection();
if (selection) {
selectedText = selection.toString();
if (selection.anchorNode) {
selectedElement = selection.anchorNode.parentNode;
}
}
}
else if (document.selection && document.selection.type === "Text") {
// For IE < 9
var selection = document.selection;
selectedText = selection.createRange().text;
}
if (!(selectedText && selectedText.length > 0) || (selectedElement !== container && !$(container).has(selectedElement))) {
setTimeout(function () { $('#chatInput').focus(); }, 0);
}
}, 0);
});
According to my tests, it works in IE (including IE7), Firefox and Chrome. The only exception is the double-click in IE, which does not select the text. You can see the result in this jsfiddle.
The calls to setTimeout ensures that all the selection processing has been done, especially when clicking on the selected text to deselect it.
Credits:
I used the method proposed by Eineki in How can I get the element in which highlighted text is in? to check if the text element contains the selected text.
The code for processing the selection in IE < 9 was found in Tim Down's answer to the post Get the Highlighted/Selected text.
A bit longer than I initially thought a solution could be but here's what I got:
var mouseDownStart = 0,
lastKeyupTime = 0;
function processKeyDown() {
if (!mouseDownStart) {
mouseDownStart = Date.now();
}
}
function processKeyUp() {
var now = Date.now(),
isDoubleClick = lastKeyupTime && now - lastKeyupTime < 500;
isHighliting = now - mouseDownStart > 150
lastKeyupTime = now;
mouseDownStart = 0;
return {
isDoubleClick: isDoubleClick,
isHighliting: isHighliting
}
}
$('#text').on('mousedown', function (e) {
processKeyDown();
});
$('#text').on('mouseup', function (e) {
var data = processKeyUp();
if (data.isDoubleClick || data.isHighliting) return;
$('#chatInput').focus();
});
Updated fiddle: https://jsfiddle.net/xhykmtwy/1/

Move caret right after input in contenteditable when caret is inside the input at the end of it

I am having this problem. I am inserting input elements in a contenteditable div. The caret is in the input at the last position. I would like some help how to move the cursor right after that input by executing some function. You will see in my code that I have to click (justInserted.click()) to make some resizing happen. If I remove the justInserted.focus() then the caret is always at the start of the contenteditable. I would like to have a function that finds that the caret is in a specific input in the contenteditable and when I call it, it will put the caret right after that specific input. Any help is appreciated :)
My insert at caret looks like this:
this.insertNodeAtCursor = function(node) {
var sel, range, html;
function containerIsEditable(selection) {
return $(selection.anchorNode).parent().hasClass("editable");
}
if (window.getSelection) {
sel = window.getSelection();
// only if it is a caret otherwise it inserts
// anywhere!
if (containerIsEditable(sel) && sel.getRangeAt
&& sel.rangeCount) {
var previousPosition = sel.getRangeAt(0).startOffset;
sel.getRangeAt(0).insertNode(node);
}
}
else if (document.selection
&& document.selection.createRange) {
range = document.selection.createRange();
html = (node.nodeType == 3) ? node.data
: node.outerHTML;
range.pasteHTML(html);
}
};
and the function that adds the input is this:
this.addInput = function(suggestEntry, query) {
var id = suggestEntry.id;
var nodeClass = suggestEntry.nodeClass;
var uuid = suggestEntry.uuid;
var clause = null;
if (nodeClass === "Entity"){
clause = new Entity();
clause.uuid = uuid;
clause.id = id;
clause.text = suggestEntry.text;
}
var input = clause.toEditorElementHtml();
this.insertNodeAtCursor(input);
var rand = Math.floor((Math.random() * 1000000) + 1);
input.setAttribute('id', "rand-" + rand);
$rootScope.$broadcast("remove:query",query);
var autoSizingInputs = $('input[autosize="autosize"]');
var justInserted = $('#rand-' + rand);
$compile(autoSizingInputs)($scope);
justInserted.focus();
justInserted.click(); // a bit hacky :/
$(justInserted).val($(justInserted).val() + "");
};
Here's a snippet with a function that moves the caret right after the focused input. I've tested it in Chrome Version 47.0.2526.111 m, Firefox 43.0.4 and IE 11.
function setCaretAfterFocusedInput(container) {
var input = container.querySelector('input:focus');
if (input) {
container.focus(); // required for firefox
setCaretAfter(input);
}
}
function setCaretAfter(element) {
if (window.getSelection) {
var range = document.createRange();
range.setStartAfter(element);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}
// for demonstration purposes
document.addEventListener('keyup', function(e) {
if (e.which === 16) { // on SHIFT
var container = document.querySelector('div[contenteditable]');
setCaretAfterFocusedInput(container);
}
});
<p>When an input is focused, press shift to move the caret right after the input.</p>
<div contenteditable>
<input type="text" value="input1"><input type="text" value="no whitespace before this">
<br><br>some text<input type="text" value="input3">more text
<br><br>
<input type="text"><p>text in a paragraph, no whitespace before this</p>
<input type="text">
<p>text in a paragraph</p>
</div>

How to get range of selected text of textarea in 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

Categories