views:

42

answers:

2

Hi folks, what is the best way to do this in jQuery? This should be a fairly common use case.

  1. User selects text in a textarea
  2. He clicks on a link
  3. The text in the link replaces the selected text in the textarea

Any code will be much appreciated - I am having some issues with part 3.

+1  A: 

Introduction to ranges

alex
Hi Alex, I need a jQuery centric solution. Thanks for the link tho!
ming yeow
@ming yeow I don't think you'll use much jQuery here, besides selecting the DOM elements you require.
alex
Hi Alex! I was trying to write my own, but had so many browser issues
ming yeow
+1  A: 

Here's how you can do it, in all major browsers. I've also got a jQuery plug-in in the works (as yet undocumented) that includes this functionality. With that, the code would be

$("your_textarea_id").replaceSelectedText("NEW TEXT");

Here's a full stand-alone solution:

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 replaceSelectedText(el, text) {
    var sel = getInputSelection(el), val = el.value;
    el.value = val.slice(0, sel.start) + text + val.slice(sel.end);
}

var el = document.getElementById("your_textarea");
replaceSelectedText(el, "[NEW TEXT]");
Tim Down
This will be an amazingly useful plugin. Thank you so much! It is kinda weird how this use case is not covered yet. I tried writing my own, but hit on so many browser quirks. ;)
ming yeow
replaceSelectedText - the only issue i see with this is that in IE, when nothing is selected, the cursor goes to the front, as opposed to the end. anyway that can be fixed?
ming yeow
In the code posted here or the jQuery plugin? The code here makes no attempt to reposition the selection afterwards, for the sake of brevity. The jQuery plugin should deal with that.
Tim Down