I want to replace selected text(or insert new text after cursor position if nothing is selected).
The new text is entered from another textbox.
I want to be able to insert new text without clicking first (focusing) in the textarea.
meaning: first select text to replace inside textarea, then enter new text into the textbox and click the button.
<textarea id='text' cols="40" rows="20">
</textarea>
<div id="opt">
<input id="input" type="text" size="35">
<input type="button" onclick='pasteIntoInput(document.getElementById("input").value)' value="button"/>
</div>
function pasteIntoInput(text) {
el=document.getElementById("text");
el.focus();
if (typeof el.selectionStart == "number"&& typeof el.selectionEnd == "number") {
var val = el.value;
var selStart = el.selectionStart;
el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
el.selectionEnd = el.selectionStart = selStart + text.length;
}
else if (typeof document.selection != "undefined") {
var textRange = document.selection.createRange();
textRange.text = text;
textRange.collapse(false);
textRange.select();
}
}
Online example:
link text
So, you want to persist the selection when you focus out of the textarea and focus on the input tag.
You need to remember the selection (start and end points when the text area loses focus) and force the selection so that it persists.
To remember the selection, you can store the el.selectionStart
and el.selectionEnd
in two global variables inside a function which is called at onblur()
event of textarea tag.
Then inside your pasteIntoInput()
you can consider those two points for replacement.
To force selection - Check this solution for persisting the selection. This uses jquery however, not plain javascript.
However, I am not sure whether the solution actually works. I tried it here http://jsfiddle.net/sandeepan_nits/qpZdJ/1/ but it does not work as expected.
Updates
I doubt if it is possible to persist selection after focus is gone. Probably selection needs focus to be there and the answer link that I gave tries to focus and then select. In that case this will not solve your problem. So, the alternatives could be -
faking a text area with an html div. You can define some styles to create a selection like effect and apply it onblur()
or use a simple readymade editor if available.
Displaying the selection dynamically in a separate area. Check this demo of jquery fieldSelection plugin . Remember that you are already storing the selection in global variables for the actual replacement. You only need to display to the user the selection which will be replaced. I think displaying the selection separately like this demo saves your time and it looks cool too.
But depends on your requirement of course.
Further Update
Check http://jsfiddle.net/sandeepan_nits/qpZdJ/2/ for a working "Replacing text inside textarea without focus" (like you want) but without the selection on blur. I still don't know whether it is possible to keep the selection on blur.
Another Update (21st December)
Working solution for IEs as well as other browsers
http://jsfiddle.net/sandeepan_nits/qpZdJ/24/
Here is the same code:-
The html -
<textarea id='text' cols="40" rows="20" onbeforedeactivate="storeSelectionIeCase();" onblur="storeSelectionOthersCase();">
</textarea>
<div id="opt">
<input id="input" type="text" size="35">
<input type="button" onclick='pasteIntoInput(document.getElementById("input").value)' value="button"/>
</div>
and all the js
var storedSelectionStart = null;
var storedSelectionEnd = null;
function pasteIntoInput(text)
{
el=document.getElementById("text");
el.focus();
if((storedSelectionStart != null) && (storedSelectionEnd != null))
{
start = storedSelectionStart;
end = storedSelectionEnd;
}
else
{
start = el.selectionStart;
end = el.selectionEnd;
}
if (typeof start == "number"&& typeof end == "number")
{
var val = el.value;
var selStart = start;
var end = selStart + text.length;
el.value = val.slice(0, selStart) + text + val.slice(end );
}
else if (typeof document.selection != "undefined")
{
var textRange = document.selection.createRange();
textRange.text = text;
textRange.collapse(false);
textRange.select();
}
}
function storeSelectionOthersCase()
{
if(!(isBrowserIE6() || isBrowserIE7()))
{
storeSelection();
}
else
{
return false;
}
}
function storeSelectionIeCase()
{
if((isBrowserIE6() || isBrowserIE7()))
{
storeSelection();
}
else
{
return false;
}
}
function storeSelection()
{
//get selection
el=document.getElementById("text");
var el = document.getElementById("text");
var sel = getInputSelection(el);
//alert("check"+sel.start + ", " + sel.end);
storedSelectionStart = sel.start;
storedSelectionEnd = sel.end;
//alert("see"+storedSelectionStart +" - "+storedSelectionEnd );
}
function getInputSelection(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;
}
}
}
}
return {
start: start,
end: end
};
}
function isBrowserIE6()
{
var ret = false;
if(($.browser.msie) && (parseInt($.browser.version) == 6) && (!this.XMLHttpRequest))
{
ret = true;
}
return ret;
}
function isBrowserIE7()
{
var ret = false;
if(($.browser.msie) && ((parseInt($.browser.version) == 7) && (this.XMLHttpRequest)))
{ //Modification because of IE tester IE7 being detected as IE6
ret = true;
}
return ret;
}
The previous fiddle was not working in IEs because by the time the onblur()
event fires, the selection is destroyed in IE. I have applied some browser based conditions for IE 6 and 7, but not tested yet in IE 8.
Thanks to Tim Down who helped me in identifying the problem with the previous fiddle.
Alright, correct me where I am wrong.
1) When text in the textarea is selected, then the button is clicked, the selected text is replaced by the text in the input.
2) When no text is selected, no matter the cursor position, text is automatically added at the very end of the textarea.
If those are the only stipulations, then this javascript would suffice, otherwise I need more information on what you want it to do.
function pasteIntoInput(text) {
el=document.getElementById("text");
el.focus();
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number" && el.selectionStart != el.selectionEnd) {
var val = el.value;
var selStart = el.selectionStart;
el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
el.selectionEnd = el.selectionStart = selStart + text.length;
}
else
el.value += text;
}
Sorry that I cannot be of more assistance, it would be beneficial to understand the use of the function, so I could give the desired action.
This is alternative solution based on answers from above and more ideas.
For IE only (other browsers support this functionality by default).
More testing is needed (I tested it just in IE8)
<script type="text/javascript">
var pos = 0;
var len = 0;
function pasteIntoInput(text) {
var el = document.getElementById("text");
el.focus();
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
var val = el.value;
var selStart = el.selectionStart;
el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
el.selectionEnd = el.selectionStart = selStart + text.length;
}
else if (typeof document.selection != "undefined") {
//var textRange = document.selection.createRange();
var textRange = el.createTextRange();
// el.focus();
if (len > 0) { //something selected, so replace
textRange.collapse(true);
textRange.moveEnd('character', pos+len);
textRange.moveStart('character', pos);
textRange.select();
textRange.text = text;
}
else {
textRange.collapse(true);
textRange.moveEnd('character', pos);
textRange.moveStart('character', pos);
textRange.text = text;
textRange.select();
}
el.focus();
}
}
function GetCaretPosition(txtarea) {
pos = 0;
if (document.selection) {
// get current selection
var range = document.selection.createRange();
if (range.text.length>0)
len=range.text.length;
// get full range
var rangeFull = document.body.createTextRange();
rangeFull.moveToElementText(txtarea);
var sel_start;
for (sel_start = 0; rangeFull.compareEndPoints('StartToStart', range) < 0; sel_start++) {
rangeFull.moveStart('character', 1);
}
pos = sel_start;
}
}
</script>
A jquery plugin to get selection index start and end in text area. The above javascript codes didnt work for IE7 and IE8 and gave very inconsistent results, so I have written this small jquery plugin.
Allows to temporarily save start and end index of the selection and hightlight the selection at a later time.
A working example and brief version is here:
http://jsfiddle.net/hYuzk/3/
A more details version with comments etc. is here:
http://jsfiddle.net/hYuzk/4/
// Cross browser plugins to set or get selection/caret position in textarea, input fields etc for IE7,IE8,IE9, FF, Chrome, Safari etc
$.fn.extend({
// Gets or sets a selection or caret position in textarea, input field etc.
// Usage Example: select text from index 2 to 5 --> $('#myTextArea').caretSelection({start: 2, end: 5});
// get selected text or caret position --> $('#myTextArea').caretSelection();
// if start and end positions are the same, caret position will be set instead o fmaking a selection
caretSelection : function(options)
{
if(options && !isNaN(options.start) && !isNaN(options.end))
{
this.setCaretSelection(options);
}
else
{
return this.getCaretSelection();
}
},
setCaretSelection : function(options)
{
var inp = this[0];
if(inp.createTextRange)
{
var selRange = inp.createTextRange();
selRange.collapse(true);
selRange.moveStart('character', options.start);
selRange.moveEnd('character',options.end - options.start);
selRange.select();
}
else if(inp.setSelectionRange)
{
inp.focus();
inp.setSelectionRange(options.start, options.end);
}
},
getCaretSelection: function()
{
var inp = this[0], start = 0, end = 0;
if(!isNaN(inp.selectionStart))
{
start = inp.selectionStart;
end = inp.selectionEnd;
}
else if( inp.createTextRange )
{
var inpTxtLen = inp.value.length, jqueryTxtLen = this.val().length;
var inpRange = inp.createTextRange(), collapsedRange = inp.createTextRange();
inpRange.moveToBookmark(document.selection.createRange().getBookmark());
collapsedRange.collapse(false);
start = inpRange.compareEndPoints('StartToEnd', collapsedRange) > -1 ? jqueryTxtLen : inpRange.moveStart('character', -inpTxtLen);
end = inpRange.compareEndPoints('EndToEnd', collapsedRange) > -1 ? jqueryTxtLen : inpRange.moveEnd('character', -inpTxtLen);
}
return {start: Math.abs(start), end: Math.abs(end)};
},
// Usage: $('#txtArea').replaceCaretSelection({start: startIndex, end: endIndex, text: 'text to replace with', insPos: 'before|after|select'})
// Options start: start index of the text to be replaced
// end: end index of the text to be replaced
// text: text to replace the selection with
// insPos: indicates whether to place the caret 'before' or 'after' the replacement text, 'select' will select the replacement text
replaceCaretSelection: function(options)
{
var pos = this.caretSelection();
this.val( this.val().substring(0,pos.start) + options.text + this.val().substring(pos.end) );
if(options.insPos == 'before')
{
this.caretSelection({start: pos.start, end: pos.start});
}
else if( options.insPos == 'after' )
{
this.caretSelection({start: pos.start + options.text.length, end: pos.start + options.text.length});
}
else if( options.insPos == 'select' )
{
this.caretSelection({start: pos.start, end: pos.start + options.text.length});
}
}
});