新しい方法を知ったので、ついでに前の方法を書き残しておく

<textarea> にタブ入力できるようにしたい時、ずっとこれ↓でやってました。

document.getElementById('my-textarea').addEventListener('keydown', event => {
	if (event.key == 'Tab' && !event.ctrlKey && !event.altKey && !event.shiftKey) {
		event.preventDefault();
		const elem = event.target;
		const start = elem.selectionStart;
		const end = elem.selectionEnd;
		const value = elem.value;
		elem.value = value.substring(0, start) + '\t' + value.substring(end);
		elem.selectionStart = start + 1;
		elem.selectionEnd = elem.selectionStart;
	}
});

が、別件で調べてて以下の記事を見つけました。

どんくさすぎてUndo/Redoができないことにすら気づいてませんでした。実際やったらできませんでした。

document.execCommand() で現在のカーソル位置にタブを挿入すれば、Undo/Redoが効くみたいです。

document.getElementById('my-textarea').addEventListener('keydown', event => {
	if (event.key == 'Tab' && !event.ctrlKey && !event.altKey && !event.shiftKey) {
		event.preventDefault();
		document.execCommand('insertText', false, '\t');
	}
});

こんなAPIあったの初めて知った。IEでも使えるらしい。

動作サンプル

上記の2つのコードを実装したテキストエリアです。