`contenteditable` + `::first-letter` is a buggy combination in Chrome

Published on – Tags:

When investigating a buggy and weird behavior of dropcap paragraphs (implemented using the ::first-letter CSS pseudo-element) in a customized TinyMCE editor (which uses the contenteditable attribute), I found that the root cause was a known but unfixed bug in Chrome.

Table of contents

Problems with the combination

At work, we had customized our TinyMCE editor to show a stylized initial letter – a dropcap – in the first paragraph. The idea was to make the text look the same in the editor as it looks on the site.

The customization was implemented using CSS like this, though in a wee bit more sophisticated way:

#tinymce > p:first-child::first-letter {
font-size: 2em;
font-weight: bold;
}

Then a bug report came in which stated that there were two problems in TinyMCE when editing a dropcap paragraph (i.e. the first paragraph): the caret position was incorrect, and sometimes there was data loss. These problems occurred only when editing dropcap paragraphs in Chrome.

After quick testing, I confirmed that I could reproduce the problems, with two different kinds of data loss:

  • Incorrect caret position: when the caret is in the dropcap paragraph, its position is one character too much to the right.
  • Minor data loss: pressing Backspace when the caret is at the beginning of the second paragraph (so that the contents of the second paragraph are moved to the end of the first paragraph) also removes the last character of the first paragraph. The same happens by pressing Delete when the caret is at the end of the first paragraph.
  • Major data loss: pressing Backspace when the caret is at the end of the dropcap paragraph empties the whole paragraph! Looking at Chrome DevTools, the <p> tag is still there, but it's empty. The same happens by pressing Delete when the caret is at the second last position of the first paragraph.

Yikes! When investigating the issue, I found that it's not related to TinyMCE per se, but present in any contenteditable element that is styled with the ::first-letter pseudo-element.

The root cause: a bug in Chrome

Long story short: this is a bug in Chrome. The "Contenteditable with :first-letter styling" Chromium issue was reported in 2013 (currently 7.5 years ago) and hasn't been fixed yet.

The fix: don't use the combination

The issue was not in our implementation, nor was it in TinyMCE. It was in Chrome itself, so there was nothing that we could reasonably do.

I suggested that we get rid of the combination, i.e. get rid of the dropcap styling in the TinyMCE editor, and that's what we did.

It wasn't that big of a loss in the end; we anyway had an accurate preview mode, and even an "on-page editing" mode which looks like the preview mode but where the editor can edit the texts in place and see the changes immediately.

How I investigated the issue

Note: this was a simple 30-minute investigation, so nothing special, but I hope that this short story of how I approached the issue will be helpful to some by seeing one way of tackling a bug report like this.

After managing to reproduce the reported problems, I investigated and solved the issue in the following steps:

  1. I first tested what styles of the ::first-letter pseudo-element cause the problems by tweaking the styles in Chrome DevTools. After quick fiddling, it was apparent that any single valid style causes the problems. Because of this, it looked unlikely that the issue was in our implementation.

  2. Thinking that this was an issue in TinyMCE, I googled for "tinymce first-letter" and similar queries to look for similar bug reports and solutions. But all I could find was one TinyMCE issue on GitHub from 2016 which was relevant but closed in 2018 without a solution.

  3. Hmm, what could I try next... We were using TinyMCE version 4 and the latest was version 5, so maybe this issue was fixed ages ago (I was still thinking that the issue is in TinyMCE), and everyone had already upgraded to version 5, so that's why no one was talking about this?

    Well, let's try. I remembered that there's a live demo of TinyMCE at its homepage. I added dropcap stylings to the demo using Chrome DevTools, and... Bleh, version 5 had the same problems.

  4. But wait! I noticed that the TinyMCE demo used the contenteditable attribute. Could it be that this was not a styling issue (despite the aforementioned GitHub issue having being closed because "it seems to be a problem with css and not Tinymce itself") but related to the contenteditable attribute?

    I created a quick 2-minute fiddle of the contenteditable + ::first-letter combo at flems.io – and voilà! My quick fiddle had the same problems.

  5. With higher hopes of finding relevant results (because the contenteditable attribute is more common than TinyMCE), I googled for "contenteditable first-letter." The first SO question that I opened had this snippet of wisdom in the top-voted answer by Marc J (thanks Marc!):

    I just noticed that there is a bug in Chrome that causes the cursor to become in the wrong spot with contenteditable and ::first-letter.

And that was it! Some fiddling, googling, and a little bit of luck.