Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing text inside textarea without focus

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

like image 870
asker Avatar asked Dec 19 '10 19:12

asker


1 Answers

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.

like image 141
Sandeepan Nath Avatar answered Oct 06 '22 01:10

Sandeepan Nath