This is a technical article covering how browsers muck up the underlying markup in an editable area as a user edits content. It turns out the problems center around opening up new lines using the ENTER key and merging them back together again using the BACKSPACE or DELETE keys.
If you dont go look you just wont know.
When you attempt to create an editor in a contenteditable div, the browser is in control and youre just along for the ride. Each browser has different ideas of what should be done to the underlying markup when a user presses ENTER to open up a new line or DELETE/BACKSPACE to merge lines back together. This conspires to make writing an editor that behaves as you would expect somewhat challenging.
In trying to develop this code, I realized that I didnt fully understand how the various browsers were interfering with the underlying markup as the user typed. I kept getting surprising results. So I decided to go look and carefully document what Ive found. Im putting this up here in the hopes that it might save someone else a bit of annoying work.
I first explored all the cases related to the ENTER key that I could think of such as:
What I found was, in many cases, the browsers did not behave at all as I would have expected. To my surprise, MSIE (10) behaved more in line with my expectations than the other browsers. WebKit was the worst but FireFox likes to muck up the works with the best of them.
The markup is what was displayed using the developer tools. In MSIE, I used FireBug lite. In Chrome I used the Google Developer Tools and in FireFox I used FireBug proper.
Here are the results upon pressing ENTER in various circumstances:
as the first character in an empty editable div
WebKit:
<DIV><BR></DIV><DIV><BR></DIV>
FireFox:
<BR _moz_dirty=""><BR _moz_dirty="">
MSIE:
<P/><P/>
NOTE: Because of the <P>s MSIE lines appear double spaced.
after opening a new line, going back to the first and entering a character
WebKit:
<DIV>t</DIV><DIV><BR></DIV>
NOTE: THE BR DISAPPEARS
FireFox:
t<BR _moz_dirty=""><BR _moz_dirty="">
NOTE: pressing ENTER once inserts a single BR but does not open a new line.
MSIE:
<P>t</P><P/>
after opening a new line and entering a character on the second line
WebKit:
<DIV><BR></DIV> <DIV>t</DIV> -> BR DISAPPEARS
FireFox:
<BR _moz_dirty="">t<BR _moz_dirty="">
MSIE:
<P/><P>t</P>
end of first line with trailing BR
(initial content<div contenteditable=true>test<BR></DIV>)
WebKit:
test1<BR> -> UNEXPECTED<DIV><BR></DIV>
NOTE: INITIAL CONTENT SHOWS ONE LINE INSTEAD OF TWO.
FireFox:
test1<BR _moz_dirty=""><BR> -> UNEXPECTED that both BR's would show up.
NOTE: INITIAL CONTENT SHOWS ONE LINE INSTEAD OF TWO.
MSIE:
<P>test1</P><P><BR></P>
NOTE: INITIAL CONTENT SHOWS TWO LINES.
end of first line with embedded BR between lines
(initial content <DIV>test1<BR>test2</DIV> )
WebKit
test1<DIV><BR>test2</DIV>
FireFox
test1<BR _moz_dirty=""><BR>test2
MSIE
<P>test1</P><P><BR/>test2</P>
adding a character to initial content of <DIV><BR></DIV>
WebKit:
<DIV>t</DIV> -> UNEXPECTED. BR gets removed even though it was not added by WebKit
FireFox:
test1<BR _moz_dirty=""><BR>test2
MSIE:
<P>test1</P><P><BR/>test2</P>
after a line of text
WebKit:
test<DIV><BR></DIV>
FireFox:
test<BR _moz_dirty=""/><BR _moz_dirty="moz"/> -> DAFUQ??
MSIE:
<P>test</P><P/>
before a line of text
WebKit:
<DIV><BR></DIV>test
FireFox:
<BR _moz_dirty="">test
MSIE:
<P/><P>test</p>
in the middle of a line of text
WebKit:
te<DIV>st</DIV> -> UNEXPECTED
FireFox:
te<BR _moz_dirty="">st
MSIE:
<P>TE</P><P>ST</p>
before an image
WebKit:
<DIV><BR></DIV><IMG SRC="...">
FireFox:
<BR _moz_dirty="" /><IMG SRC="...">
MSIE:
<P/><P><IMG SRC="..."</P>
after an image
WebKit:
<img src="..."><DIV><BR></DIV>
FireFox:
<IMG SRC="..."><BR _moz_dirty="" /><BR _moz_dirty="" type="_moz" /> -> DAFUQ??
MSIE:
<P><IMG SRC="..."></P><P/>
between two images
WebKit:
<img src="..."><DIV><img src="..."></DIV> -> UNEXPECTED
FireFox:
<IMG SRC="..."><BR _moz_dirty="" /><IMG SRC="...">
MSIE:
<P><IMG SRC="..."></P><P><IMG SRC="..."></P>
before a span Using <SPAN>test</SPAN> as initial editable content.
WebKit:
<DIV><SPAN><BR></SPAN></DIV> -> UNEXPECTED
NOTE: can be explained by noting the cursor cannot be moved outside of the span in WebKit
FireFox:
<SPAN><BR _moz_dirty="" />test</SPAN> --> UNEXPECTED
MSIE:
<P><SPAN/></P> <P><SPAN>TEST</SPAN></P> --> UNEXPECTED
after a span
WebKit:
<SPAN>test</SPAN<DIV><SPAN><BR></SPAN></DIV> -> UNEXPECTED
NOTE: can be explained by noting the cursor cannot be moved outside of the span in WebKit.
FireFox:
<SPAN>test<BR _mod_dirty=""/><BR _moz_dirty="" type="_moz" /> --> DAFUQ?? UNEXPECTED</SPAN>
MSIE:
<P><SPAN>test</SPAN></P><P><SPAN/></P>
in the middle of a span
WebKit:
<SPAN>te</SPAN><DIV><SPAN>st</SPAN></DIV>
FireFox:
<SPAN>te<BR _moz_dirty=""/>st</SPAN>
MSIE:
<P><SPAN>te</SPAN></P><P><SPAN>st</SPAN></P>
The other problematic case seems to be when lines are merged back together through the use of the DELETE or BACKSPACE keys. (The same situation likely arises in a multi-line selection thats typed over.) In the tests I move the cursor to the end of the first line and press DELETE to merge the lines together.
merging two lines of text separated by a <BR>
(initial content test1<BR>test2)
WebKit:
test1test2 (as two separate text nodes)
FireFox:
test1test2 (as a single text node)
MSIE
test1test2 (as a single text node)
two spans separated by a BR
(initial content <span>test1</span><BR><span>test2</span>)
WebKit
<span>test1</span><span>test2</span> -> BR got deleted. inconsistent behavior
This seems like inconsistent behavior because typically you cannot get behind a <SPAN> in webkit.
FireFox
<span>test1</span><span>test2</span>
MSIE
<span>test1</span><span>test2</span>
text followed by a BR and SPAN
(initial content test1<BR><span>test2</span>)
WebKit
test1<span>test2</span>
FireFox
test1<span>test2</span>
MSIE
test1<span>test2</span>
text followed by a div
(initial content test1<div>test2</div>
WebKit
"test1""test2" as two separate text nodes
FireFox
test1test2 as a single text node
MSIE
test1test2 as a single text node
two divs
(initial content <div>test1</div><div>test2</div>)
WebKit
<div>test1test2</div> (as two text nodes
FireFox
<div>test1test2</div>
MSIE
<div>test1test2</div>
text followed by <BR> followed by DIV
(initial content test1<br><div>test2</div>)
WebKit
test1test2 as two text nodes --> UNEXPECTED, but makes sense after a bit of thought.
FireFox
test1test2
MSIE
test1test2
divs ending in BR
(initial content <div>test1<br></div><div>test2<br></div>
WebKit
<DIV>test1test2 as two text nodes --> UNEXPECTED. BOTH BRs gone.</DIV>
FireFox:
<DIV>test1test2<BR></DIV>
MSIE First DELETE has no visible effect on screen but consumes the BR:
<div>test1</div><div>test2<br></div>
There are obviously many other test cases that could be explored. Interestingly, as I was formatting this article I noticed that the WordPress WYSIWYG editor suffers from some of the same problems Ive identified above.
Merging lines turned out not to be as unexpected as I had feared however it did provide the interesting insight that MSIE and FireFox apparently merge multiple adjacent text nodes together while WebKit does not. This explains at least one bug Im seeing in my code.
You must be a member of this group to post comments.
Please see the top of the page to join.