<textarea id="target-textarea" spellcheck="false">Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet. Test lorem ipsum dolor sit amet.</textarea> <button onclick="_tab(document.getElementById('target-textarea'));">Tab</button> <button onclick="_untab(document.getElementById('target-textarea'));">Untab</button>
(function() { var tabCharacter = ' '; // Use `\t` or multiple space character var select = function(start, end, target) { target.focus(); target.setSelectionRange(start, end); }; window._tab = function(target) { var start = target.selectionStart, end = target.selectionEnd, value = target.value, selections = value.substring(start, end).split('\n'); for (var i = 0, len = selections.length; i < len; ++i) { selections[i] = tabCharacter + selections[i]; } target.value = value.substring(0, start) + selections.join('\n') + value.substring(end); // re-select text after tabbing var selectEnd = (end + (tabCharacter.length * selections.length)); if (start == end) { select(selectEnd, selectEnd, target); } else { select(start, selectEnd, target); } }; window._untab = function(target) { var start = target.selectionStart, end = target.selectionEnd, value = target.value, pattern = new RegExp("^" + tabCharacter), edits = 0; if (start == end) { // single line while (start > 0) { if(value.charAt(start) == '\n') { start++; break; } start--; } var portion = value.substring(start, end), matches = portion.match(pattern); if (matches) { target.value = value.substring(0, start) + portion.replace(pattern, "") + value.substring(end); end--; } // set caret position after tabbing var selectEnd = end <= start ? end : end - tabCharacter.length + 1; select(selectEnd, selectEnd, target); } else { // multiline var selections = value.substring(start, end).split('\n'); for (var i = 0, len = selections.length; i < len; ++i) { if (selections[i].match(pattern)) { edits++; selections[i] = selections[i].replace(pattern, ""); } } target.value = value.substring(0, start) + selections.join('\n') + value.substring(end); // re-select text after tabbing select(start, (edits > 0 ? end - (tabCharacter.length * edits) : end), target); } }; })();