Search code examples
htmlfirefoxcontenteditable

ContentEditable in Firefox creates 2 newlines instead of 1


I've just noticed that div contenteditable in reports 1 newline as 2 newlines in Firefox.

Is this a bug or am I missing something?

In the following example just type:

Hello

World

in the contenteditable.

Grabbing the value using innerText reports it as:

Hello



World

const textarea = document.querySelector('#textarea')

textarea.addEventListener('keydown', e => {
  console.log(textarea.innerText)
})
#textarea {
  width: 200px;
  height: 75px;
  outline: 1px solid black;
}
  <div id="textarea" contenteditable="true"></div>


Solution

  • This appears to be a bug with Firefox's implementation of innerText.

    New lines in contenteditable historically had a lack of consensus on the precise implementation. Gecko inserted <br>s, IE and Opera's Presto wrapped the line in <p> tags, and Webkit and Blink wrapped the line in <div> tags. With its old implementation that used <br> tags, Firefox's innerText worked just fine, exactly how your question wants it to.

    However today's modern browsers have reached a general consensus for the sake of consistency. Specifically for Firefox, MDN stated:

    As of Firefox 60, Firefox will be updated to wrap the separate lines in <div> elements, matching the behavior of Chrome, modern Opera, Edge, and Safari.

    When modern browsers insert an empty new line, they do so by wrapping a <br> in <div> tags. It seems that Firefox's innerText implementation is interpreting this as two new lines.


    A workaround is quite simple. The execCommand method (unofficial W3 draft but supported by Firefox which is all we need) allows you to manipulate a contenteditable's content with defined commands. One such command is defaultParagraphSeparator, which allows you to specify whether to use <div> or <p> as the container for lines in your contenteditable.

    Firefox - and only Firefox - has additionally implemented support for <br>s with defaultParagraphSeparator. With this command the contenteditable behaves like it would have prior to FF60, that is, inserting line breaks instead of wrapping lines within a container.

    Thus, all you need to do is put:

    document.execCommand("defaultParagraphSeparator", false, "br");
    

    in your JavaScript. All versions of Firefox will then be using <br>s instead of containers for new lines in contenteditable, thus making innerText correctly interpret new lines. And every browser other than Firefox will ignore it.