7

I'm writing a Chrome Extension that needs to detect contenteditable HTML elements (or the elements where a user can type into) on the page where my context script is injected into.

I'm currently doing this:

var objAll = document.getElementsByTagName("*");
for(var i = 0; i < objAll.length; i++)
{
    obj = objAll[i];

    if(obj.contentEditable &&
        obj.contentEditable != 'inherit' &&
        obj.contentEditable != 'false')
    {
        //Yes, this is a content editable element!
    }
}

But my method doesn't seem to work in all the sites that I tested it on.

I'm curious, what am I missing there?

PS. Because it's a content script I am not using jQuery to make it more robust.

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • Have you tried selecting all the elements you want at once with something like `document.querySelectorAll('[contenteditable]')`? – MaxArt Sep 10 '14 at 07:54

1 Answers1

14

contentEditable is a property that's implied by the contenteditable attribute. What you really need to check is the isContentEditable property, which is a boolean that tells if the element has an editable content or not:

if (obj.isContentEditable) {
    // do stuff
}

But instead of getting all the elements and filtering them, just select all the contenteditable elements:

var contEditables = document.querySelectorAll('[contenteditable]');
for (var i = 0; i < contEditables.length; i++) {
    // do stuff
}

In fact, an element has editable content if and only if it has a contenteditable attribute, whether is an empty string or not.

Live demo

MaxArt
  • 22,200
  • 10
  • 82
  • 81
  • Thanks. You know, for some reason `document.querySelectorAll('[contenteditable]')` doesn't do it. Only when I do `if (obj.isContentEditable)` instead of what I had, and keep `document.getElementsByTagName("*")`, it works for my specific test page. Any idea why? – c00000fd Sep 10 '14 at 08:26
  • @c00000fd Actually, no. That query should and [does work](http://jsfiddle.net/m8jouzpd/), and although I haven't tested it in an extension, I don't see why it should behave differently. – MaxArt Sep 10 '14 at 08:36
  • Thanks for the demo. I can see that. But I'm testing it with a much more complex example in a live page (from the Chrome extension) and what I said above **does** make a difference. And I'd appreciate if someone can explain why? – c00000fd Sep 10 '14 at 08:42
  • Why? Maybe elements are created dynamically? When do you run that code? – Xan Sep 10 '14 at 08:45
  • @c00000fd It's hard to tell without a real case. The only thing I can say is that `getElementsByTagName` returns a *live* collection, so it changes when new elements are added, while `querySelectorAll` is a *snapshot* of a query on the DOM. So you have to call it again if some elements have been added in the meanwhile. – MaxArt Sep 10 '14 at 08:46
  • @Xan: Quite possibly they could've been created dynamically. I'm not the author of the page that the script runs on. So, what you're saying is that if elements were created dynamically then I'll need to call `document.getElementsByTagName("*")`, right? – c00000fd Sep 10 '14 at 08:47
  • @c00000fd I direct you to this question: http://stackoverflow.com/questions/2844565/is-there-a-jquery-dom-change-listener – Xan Sep 10 '14 at 08:48
  • @Xan: Yeah, I know about mutation observer. Still, it's a different subject. – c00000fd Sep 10 '14 at 08:50
  • FYI, here's the page I'm testing it on: http://music.msn.com/music/article.aspx?news=888163 The 'contenteditable' element is the `Write a comment...` field at the bottom of the page. It's hard to tell if it is created dynamically. – c00000fd Sep 10 '14 at 09:11
  • 1
    @c00000fd Ah, careful, that's something different! That's the *whole document* (in a ` – MaxArt Sep 10 '14 at 09:18
  • Thanks for the info. So you're saying I need to check for `obj.designMode` on retrieved DOM elements? – c00000fd Sep 10 '14 at 09:25
  • @c00000fd Nope, not on the elements: that property is defined on document objects. So, if you have an iframe, you have to get its `contentDocument`. – MaxArt Sep 10 '14 at 09:34