Code editor meets ‘contenteditable’ — a failed project
There are many code editors in today’s tech websites. Most of the code editors are doing almost the same things — code highlighting, language parsing, auto indentation, etc. Some even fancier ones provide you a cloud editor IDE with more close-to-native features to make developers life easier (or harder?). I wasn’t thinking a lot when using any one of them, until recently I needed to write one for a inline expression builder component in our project. The findings and thoughts for the past two weeks made me re-think how bad and good our today’s web technologies could be.
Long story short, using ‘contenteditable’ for code editor sounds interesting, but the devil is in details — The result of many reverse engineered ‘contenteditable’ implementations in different browsers can make the development and debug cycles very painful. You are almost for sure going to regret on using this piece of black magic after all the quirks that follows.
Here is the deal: as simple as it looks like, a inline expression builder/editor needs to support editing, parsing, syntax hints as well as autocomplete for our in-house expression language. I know CodeMirror or Ace editor are all popular and powerful, but they are also very expensive in terms of size and customisation. Also as we already have a server side parser that maybe reusable for front end to parse expressions, which is not compatible by both as they need their own language modes to work. I decided to write one by myself.
That’s when ‘contenteditable’ became interesting to me. To quickly get the editor to work this seems a short path. A recent popular github repo named pell (I found it actually after I started question myself on using ‘contenteditable’) shows how simple it could be to make a web editor. Sounds like I can edit the HTML of an element as I wish, which allows me to add syntax highlighting after parsing the expressions.

The first issue is to define ‘clean HTML’. ‘contenteditable’ was first introduced by Microsoft in their IE browser (IE 5.5). Since there were no clean standards on how the HTML should look like inside a ‘contenteditable’ editor, different browsers started to reverse engineer IE and supported it in each of their own way. To name a few, IE uses ‘<p>’ for paragraphs or lines, while chrome uses ‘<div>’ with ‘<br>’ in it. To make it more consistent, firefox joined by using only ‘<br>’s next to text nodes. In order to make sure our editor shows indents and highlights consistently, I want all rows are ‘<div>’s, and all new lines are ‘<br>’s.
Having the clean HTML defined, the second issue I met was to read the actual text content from this little editor that I created. Since the editor needs to support multiple lines and needs to parse the expressions and render the HTML contents, both ‘textContent’ and ‘innerText’ won’t work. By design ‘textContent’ property should return the text without new lines, and ‘innerText’ is non-standard as well as inconsistent in parsing both ‘<br>’s and ‘<div>’s. After reading several stack-overflow posts I ended up with writing a node traversal function to exam each node, and read out text nodes and new lines by myself. This sounds simple and actually pretty straight-forward when got it work in chrome, but when I switch to firefox everything stops working — firefox dislike the fact of ‘<div>’ inside its ‘contenteditable’ element.
It took me surprisingly long time to fix this issue as well as all the others. I have to use ‘<br>’ to get firefox up/down arrow key working. I couldn’t hit backspace in an empty row, as chrome will remove the previous row’s ‘<br>’ element. When I wanted to copy and paste the result will be different depends on where I paste my content in, as it ended up with different ‘dirty HTML’ in different browsers. I can keep on listing them but to make this less like just making complaints here, I would say it’s just too many possible edge cases because of the different implementations of ‘contenteditable’ in different browsers.
After almost got things to work, I realised that the build-in keyboard commands (like ctrl-b/cmd-b for bold text) are something I may not need in my editor. As this stack-overflow answer mentioned, I could just stop it using JS, but after reading the post it refers to about how ‘contenteditable’ was started and evolved, I became more worried about my approach.
The original purpose of ‘contenteditable’ attribute was to provide rich text editing. And the way it works is via ‘document.execCommand’ API. With over 10 years of support in varies browsers without a clean standard, the actual details on how each browser understands the command and how it renders the content are hidden so deep in the code. I tried to mess up with the rendering for the past two weeks and learned this in a hard way.
However, using ‘contenteditable’ in a text editor like the one you are reading — medium editor, is inline with the original purpose of this attribute and is possible to get job done. Even with the success of medium editor, here are a couple posts that criticizing ‘contenteditable’ on the non-standard and messed up implementations.
