I have a <textarea>
element that I listen for certain key presses from. Like if a user types the Tab key, I prevent the default action of changing focus and add the tab character at the correct position.
The problem is when users press one of the keys I listen for, undo goes a little haywire. How do I get the undo/redo functionality to work? I thought about listening for ctrl/cmd-z and ctrl/cmd-shift-z key presses, recording everything, and processing the undos/redos, but then the edit and context menu choices wouldn't work...
You can see by typing letters with tabs and enters and then trying to undo and redo:
const textarea = document.querySelector('textarea')
textarea.addEventListener('keydown', function (event) {
if (event.key == "Tab") {
event.preventDefault()
const cursor = textarea.selectionStart
textarea.value = textarea.value.slice(0, cursor) + '\t' + textarea.value.slice(textarea.selectionEnd)
textarea.selectionStart = textarea.selectionEnd = cursor + 1
} else if (event.key == "Enter") {
event.preventDefault()
const cursor = textarea.selectionStart
textarea.value = textarea.value.slice(0, cursor) + '\n' + textarea.value.slice(textarea.selectionEnd)
textarea.selectionStart = textarea.selectionEnd = cursor + 1
}
})
<textarea cols="50" rows="20"></textarea>
I have a <textarea>
element that I listen for certain key presses from. Like if a user types the Tab key, I prevent the default action of changing focus and add the tab character at the correct position.
The problem is when users press one of the keys I listen for, undo goes a little haywire. How do I get the undo/redo functionality to work? I thought about listening for ctrl/cmd-z and ctrl/cmd-shift-z key presses, recording everything, and processing the undos/redos, but then the edit and context menu choices wouldn't work...
You can see by typing letters with tabs and enters and then trying to undo and redo:
const textarea = document.querySelector('textarea')
textarea.addEventListener('keydown', function (event) {
if (event.key == "Tab") {
event.preventDefault()
const cursor = textarea.selectionStart
textarea.value = textarea.value.slice(0, cursor) + '\t' + textarea.value.slice(textarea.selectionEnd)
textarea.selectionStart = textarea.selectionEnd = cursor + 1
} else if (event.key == "Enter") {
event.preventDefault()
const cursor = textarea.selectionStart
textarea.value = textarea.value.slice(0, cursor) + '\n' + textarea.value.slice(textarea.selectionEnd)
textarea.selectionStart = textarea.selectionEnd = cursor + 1
}
})
<textarea cols="50" rows="20"></textarea>
Share
Improve this question
asked Jun 10, 2017 at 9:25
at.at.
52.5k105 gold badges303 silver badges470 bronze badges
4
- it's working for me when i run it above, i'm using firefox – inarilo Commented Jun 10, 2017 at 9:28
- it's working for me also same code, Google Crome Version 59.0.3071.86 (Official Build) (64-bit) – Santosh Commented Jun 10, 2017 at 9:30
- 1 That's interesting it's working for you guys. It consistently fails for me on Google Chrome Version 59.0.3071.86 (Official Build) (64-bit), on macOS 10.12.5. I just tried on the latest Firefox and undo/redo works great! – at. Commented Jun 10, 2017 at 19:26
- 7 years later and not working in Chrome... – Jay Sullivan Commented Oct 19, 2024 at 21:01
1 Answer
Reset to default 19I believe the problem's core is the lack of interaction between JavaScript and the browser's default methods of undoing. Appending text using JavaScript to a textarea does not in any way tell the browser's "undo" to delete the appended text, as the browser's "undo" is only meant for removing text the user inputted, not text JavaScript inputted.
Take for example your code. Upon pushing Enter, you tell the event listener to preventDefault
, which altogether prevents the Enter key from appending user input to the textarea. You then synthesize the input using JavaScript, which the browser's "undo" does not keep track of.
You can overcome this lack of interaction by using document.execCommand()
. You can check it's browser support via the link.
const textarea = document.querySelector('textarea');
textarea.addEventListener('keydown', function (event) {
const cursor = textarea.selectionStart;
if (event.key === "Tab") {
event.preventDefault();
// appends a tab and makes the browser's default undo/redo aware and automatically moves cursor
document.execCommand("insertText", false, '\t');
} else if (event.key === "Enter") {
event.preventDefault();
document.execCommand("insertText", false, '\n');
}
});
<textarea cols="50" rows="8"></textarea>