Lately, I’ve been quite amazed by this one HTML attribute I’ve been ignoring all these years: ContentEditable. While it has been around for years as the preferred way of implementing WYSIWYG rich text editors, I’ve always stuck with the boring textarea.

All this changed last week as I was coding a Chrome extension to do dynamic content insertion into the Gmail composition area. As textareas must contain well… text, there would be no way to decide on using images or hyperlinks anytime in the future if I restricted myself to ASCII. I ended up using a single fixed-dimension <div> with “contenteditable” set to true.

Working with ContentEditable is a whole new beast. I’m not sure yet whether all browsers implement the same standard tags for formatting but Chrome uses <div><br /></div> for double-line breaks and wraps paragraphs in <div>...</div>. Translating from plain text to this format meant I had do the parsing/splitting and tag insertion myself. The Gmail composition area formats its contents in a very specific way and I had to adhere to this standard.

Another thing about Gmail. Don’t bother trying to add classes and/or IDs to any of your inserted elements. Gmail strips them out. I found this out the hard way after saving and refreshing an email caused all my custom styles to be lost, despite the CSS being injected correctly. Now I understand the pain newsletter writers have to go through to get their content formatted and styled with inline-CSS. I haven’t had to write such ugly markup since 1997.

11 responses to “ContentEditable and Gmail”

  1. Have you posted this extension? I am also trying to write an extension that modifies the body of an email. Can you tell me how you get a reference to the contentEditable area (which seems to be the body of a document within an iframe) from your extension content script?

  2. Hey EZ. This post actually refers to an extension I’ve written for work. You can check it out at http://yesware.com. To get a reference to the contentEditable area of Gmail, you’ll want to do $(“form iframe”).contents().find(“body”). Hope that helps.

  3. Thanks for the quick response! That reference works, but I am actually trying to attach a keyup event to the contentEditable element and it just does not seem to work. I saw in your yesware code that there is a comment about gmail and jquery events and you have a fireevent method. Can you elaborate on that? I tried playing around with fireevent and the ‘onKeyUp’ event, but no luck so far…

    I’ll play around with it some more.

  4. The fireEvent was a workaround for the fact that $(selector).trigger(“click”) did not work. As for your issue, I think keydown/keypress may not work either if you’re really trying to check for content change: http://stackoverflow.com/questions/1391278/contenteditable-change-events. You could give the non-jquery “element.onkeydown” a try anyway (http://www.w3schools.com/jsref/event_onkeydown.asp).

    If nothing works, I may resort to some form of timer checking. On content editable focus, check every 1/2 second for any changes to the contenteditable body.

  5. Thanks again. I will try out your advice and see if I can get it to work.

  6. I finally got it working. I believe the iframes get created dynamically when you start composing an e-mail, and even though I was using the jQuery live() function to attach the event, it was not working. I now check for the iframe to appear on a timer and then attach the keyup event once when a new iframe is found. The extension is in the early stages, but if you want to check it out, it is called ezAutoCorrect for Gmail: https://chrome.google.com/webstore/detail/fholdolknchpdbajhpdenookmikblmda

  7. Sweet. I’m glad you’ve got it working. I’ll download it and check it out. Do you keep a blog or have a twitter handle?

  8. I’m on twitter as ezanker

  9. Hello ,
    Following code doesn’t always return the iframe content .
    $(“form iframe”).contents().find(“body”).html()
    Sometimes it returns the iframe content and sometimes it returns null.

  10. @Ching

    The content body would only be non-null if you are on a composer page. (where you are composing an email)

    From where are you trying to fetch the content?

  11. Yes, I’m on composer page. I’m trying to fetch the content from content_script page.

    On click of extension icon a small popup menu opens up. I send the request from this pop menu (popup.html page) to content_script page. But on content script Sometimes it returns the iframe content and sometimes it returns null.

Leave a comment