Jaime Still Jaime Still - 1 month ago 20
AngularJS Question

Textarea selection in IE 11 not working appropriately on last line

I'm building an Angular directive that consists of a textarea for writing Markdown, and buttons that insert formatting into the text area. When clicked, if no text is currently selected, a button (bold, for instance) should append the following:

**replace text**


where replace text is selected.

It is working as expected in every scenario save the following: In IE 11, when the selection occurs on the final row, but is not the first row. It works as expected in every other browser, and works fine in IE 11 minus this condition.

Here is the code from the directive for performing the selection:

var editor = element.find('textarea')[0];

function createWrappingMarkdown(symbol) {

// If text is selected, wrap that text in the provided symbol
// After doing so, set the cursor at the end of the highlight,
// but before the ending symbol(s)

/* This section works fine */

if (editor.selectionStart < editor.selectionEnd) {
var start = editor.selectionStart,
end = editor.selectionEnd;

var value = editor.value.substring(0, start) + symbol +
editor.value.substring(start, end) + symbol +
editor.value.substring(end, editor.value.length);

scope.$evalAsync(function () {
editor.value = value;
editor.focus();
editor.selectionStart = end + symbol.length;
editor.selectionEnd = end + symbol.length;
});

// If no text is highlighted, insert {symbol}replace text{symbol}
// at the current cursor position.
// After inserting, select the text "replace text"

/* This is where the selection is broken in IE 11 */

} else if (editor.selectionStart || editor.selectionStart === 0) {
var start = editor.selectionStart,
message = "replace text";

var value = editor.value.substring(0, start) + symbol +
message + symbol + editor.value.substring(start, editor.value.length);

scope.$evalAsync(function () {
editor.value = value;
setCursorSelect(start + symbol.length, start + symbol.length + message.length);
});
}
}

function setCursorSelect(start, end) {
editor.focus();
if (editor.setSelectionRange) {
editor.setSelectionRange(start, end);
} else {
editor.selectionStart = start;
editor.selectionEnd = end;
}
}


Update

See answer for the fix to this issue. The Plunk has been revised to demonstrate this fix.

Answer

After debugging further in IE, I found that editor.selectionStart was being set to a value higher than editor.value.length whenever the cursor was at the last available position in the textarea. This was only happening in IE, and not the other browsers. With this in mind, I was able to come up with the following solution whenever a selection is needed following a button press:

scope.$evalAsync(function () {
    if (editor.value.length < editor.selectionStart) {
        start = editor.value.length;
    }

    editor.value = value;
    setCursorSelect(start + symbol.length, start + symbol.length + message.length);
});

The plunk above has been updated to reflect this fix.