1

Consider the following problem:

Have a textarea like so:

<textarea id="body" name="body"></textarea>

Also have some simple JavaScript (jQuery) that inserts some new text into the textarea so a user can embed an image:

$('textarea').val($('textarea').val() + '[img]path to image file[/img]');

The trick is to automatically highlight the text in between the [img][/img] tags after that text is inserted so the user can just copy and paste their image URL in quickly, instead of manually selecting, then copy and pasting.

I've racked my brain and gone all over the internet trying to figure this out, and the best I could do was this popular StackOverflow question Selecting text in an element (akin to highlighting with your mouse) which only addresses the problem of selecting the text inside an ENTIRE element, which is not what is desired here. The problem is to select text that matches a certain string, in this case path to image file, so the user can just copy/paste. (not sure if this is the best way to do it, but that's what I thought of...).

Is this possible? I'm guessing we're going to need getSelection() and createRange() but other than that I have no idea where to go... any JavaScript wizards figured this one out already? I feel like this could be a popular question. Using jQuery is fine, as I'm already using it on the rest of the document.

Community
  • 1
  • 1
wnajar
  • 747
  • 1
  • 7
  • 27
  • I'm also wondering if this is possible without a contenteditable div or similar solution as is used in many WYSIWYG editors. – wnajar Mar 17 '13 at 15:20
  • 1
    This may help (onfocus, programatically select a range of text in an input): http://stackoverflow.com/questions/646611/programmatically-selecting-partial-text-in-an-input-field – tymeJV Mar 17 '13 at 15:50

2 Answers2

1

I actually figured this one out myself... I used the Rangy library https://code.google.com/p/rangy/ and code like this:

 // Add text to the reply area at the very end, and move the cursor to the very end.
function insertText(textarea, text) {
    textarea = $(textarea);
    textarea.focus();
    textarea.val(textarea.val() + text);
    textarea.focus();
    // Trigger the textarea's keyup to emulate typing.
    textarea.trigger("keyup");
}

// Add text to the reply area, with the options of wrapping it around a selection and selecting a part of it when it's inserted.
function wrapText(textarea, tagStart, tagEnd, selectArgument, defaultArgumentValue) {
textarea = $(textarea);
    // Save the scroll position of the textarea.
    var scrollTop = textarea.scrollTop();
    // Work out what text is currently selected.
    var selectionInfo = textarea.getSelection();
    if (textarea.val().substring(selectionInfo.start, selectionInfo.start + 1).match(/ /)) selectionInfo.start++;
    if (textarea.val().substring(selectionInfo.end - 1, selectionInfo.end).match(/ /)) selectionInfo.end--;
    var selection = textarea.val().substring(selectionInfo.start, selectionInfo.end);
    // Work out the text to insert over the selection.
    selection = selection ? selection : (defaultArgumentValue ? defaultArgumentValue : "");
    var text = tagStart + selection + (typeof tagEnd != "undefined" ? tagEnd : tagStart);
    // Replace the textarea's value.
    textarea.val(textarea.val().substr(0, selectionInfo.start) + text + textarea.val().substr(selectionInfo.end));
    // Scroll back down and refocus on the textarea.
    textarea.focus();
    // If a selectArgument was passed, work out where it is and select it. Otherwise, select the text that was selected
    // before this function was called.
    if (selectArgument) {
        var newStart = selectionInfo.start + tagStart.indexOf(selectArgument);
        var newEnd = newStart + selectArgument.length;
    } else {
        var newStart = selectionInfo.start + tagStart.length;
    var newEnd = newStart + selection.length;
    }
    textarea.selectRange(newStart, newEnd);
    // Trigger the textarea's keyup to emulate typing.
    textarea.trigger("keyup");
}

var bbcode = {
    bold: function(id) {wrapText($("textarea"), "[b]", "[/b]", "", "bolded text");},
};

Example usage:

bbcode.bold();

Full code (in a larger project I did): https://github.com/wnajar/textarea

wnajar
  • 747
  • 1
  • 7
  • 27
1

You could use my jQuery plug-in. It works around browser differences in textarea selection manipulation and has some convenience methods:

https://code.google.com/p/rangyinputs/

For your example, the code would be

var $textarea = $("#body");
var text = "path to image file"
$textarea.replaceSelectedText(text, "select");
$textarea.surroundSelectedText("[img]", "[/img]");

Demo: http://jsfiddle.net/P8Jrh/1/

Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • This doesn't work, it prints instead: `path to image file[img][/img]` and then doesn't highlight anything. Seems like a good approach though, any idea what could be wrong? I'm not sure I would be able to look through your whole source code to figure this out. – wnajar Mar 18 '13 at 06:51
  • @wnajar: Sorry, you're right: I rather arrogantly didn't test it and misremembered the API. Fixing... – Tim Down Mar 18 '13 at 10:04
  • @wnajar: Also, the API doesn't make this as easy as I'd like. I'm going to have to do an update later. – Tim Down Mar 18 '13 at 10:07
  • Downvote seems a bit harsh, expecially since this now works and makes more sense than the accepted answer. – Tim Down Mar 20 '13 at 09:23
  • @wnajar: I updated my plugin to simplify this a little and updated this answer. – Tim Down Mar 31 '13 at 17:01
  • I updated and selected yours as the answer. Both solutions work, but yours is neater. – wnajar Apr 01 '13 at 06:30