I'd like to create a contenteditable
element with CSS set to white-space: pre-wrap
, where the default behavior on pressing Enter is simply to insert a newline (the element's textContent
should then exactly match its appearance).
Having gone through several attempts at getting this to work properly, this is still causing me huge headaches. By default, inserting a newline at the end of such an element:
- Fails to make the newline visible, even with
white-space: pre-wrap
- Fails to allow the user to place the caret at the newline position
- Fails to include the newline in the element's
textContent
My best attempt so far is the following:
const trg = document.querySelector('#trg')
const insertNewline = e => {
document.execCommand('insertHTML', false, '\n!')
document.execCommand('delete')
}
trg.addEventListener('keypress', e => {
if (e.key === 'Enter') {
e.preventDefault()
insertNewline(e)
}
})
trg.addEventListener('input', e => {
const { currentTarget } = e
const { textContent, innerText } = currentTarget
console.log({ textContent, innerText })
})
trg.focus()
#trg {
white-space: pre-wrap;
}
<div id="trg" contenteditable></div>
I've logged both textContent
and innerText
for comparison — innerText
is of little use in this case, because a single keypress adds two \n
s to it for some reason.
Without the insertNewline
logic or without the immediately deleted !
, it requires two keypresses to insert a single newline at the end of the input.
It's unclear why this is the case, and more importantly, the immediately deleted character messes up the element's undo history — press Ctrl+Z end you end up with an unwanted !
. The !
can be replaced with basically any character, including something like a zero-width space, but I've left it as something visible to best illustrate the undo history problem.
Any thoughts on how to improve this?