Running on :empty

Published 16 years, 7 months past

While kicking around some ideas at the CSS workshop I led in London, I ran into some interesting problems with :empty.  If you’re not up on what that one does, it’s a way of selecting elements that have no children, including text.  To quote the formal definition:

The :empty pseudo-class represents an element that has no children at all. In terms of the DOM, only element nodes and text nodes (including CDATA nodes and entity references) whose data has a non-zero length must be considered as affecting emptiness; comments, PIs, and other nodes must not affect whether an element is considered empty or not.

So kindly consider:

*:empty {
   padding: 0.25em;
   outline: 1px dashed red;
   background: yellow;
}

We (re)discovered something very strange right there in the workshop: given the preceding rule, Firefox applies it to the body element—which is, quite clearly, not empty.  You can check out the fun on my bare *:empty test page.  From there, we realized that if you prepend a body to the selector, yielding body *:empty, the problem vanishes, as you might expect.

As it turns out, this is a known bug in Gecko.  It’s been known for about 18 months, in fact.  So that’s why I was confused by Firefox’s behavior.

The other problem is that :empty in Firefox matches img elements, whether or not they cause an image to successfully load.  You can see this happen on the same test page.  While it’s true that img is an empty element, it’s still bringing in content, so from a common-sense point of view it seems that :empty ought not to match.

In re-reading the description from the specification, though, I can’t justify my common-sense interpretation—an img element has neither element nodes nor text nodes, so far as I’m aware.  The same is true for form inputs, even text inputs that have pre-filled value text; but not for textareas that have pre-filled content text.  But then br elements should also be selected by this rule, and they apparently don’t get matched at all, as the test page shows.  Bwuh?

Well, maybe it’s a bug.  What concerns me here is that the definition of :empty basically requires that things like images and inputs always be selected, which strikes me as kind of useless.  After all, if I wanted to select all images, I’d write img into my selector; ditto input for inputs.

My thinking is that the description should change to state that application of :empty is restricted to non-replaced elements.  Alternatively, it could be redefined to only apply non-replaced elements and to empty replaced elements that don’t either cause something to load or have values associated with them.

Which seems a better solution to you, and why?  Or, if you see a better approach than either of those, what is it?


Comments (23)

  1. Empty seems to be quite a confusing term here, I’d rather see it separated into a couple of more useful things. One would be a way of selecting text-only nodes.

  2. Meyer–

    Playing the devil’s advocate, since it appears the pseudo-class empty is not supported by Opera or IE, why worry about it, with arguments of principle aside and providing that I am not overlooking some practical uses of it.

    On theoretical purposes, possibly, the latter definition you provided.

    Logic: I am a guy and at the moment, more is better.

  3. Did I just stick my foot in my mouth? Your test example doesn’t work in Opera or IE. I have no direct knowledge whether :empty works in Opera or IE. Just ran your example through both to try to gain better understanding of a CSS element of which I was not familiar until your post.

  4. I don’t have anything like your depth of experience here, but it seems to me that what I’d like is to select any element which could have children, based on the DTD, but doesn’t. I don’t think (haven’t actually checked) that a well-formed img element can have children, so I wouldn’t expect it to be selected. Ditto img, hr, and anything else defined as <!ELEMENT foo EMPTY>.

    This would mean that a textarea with pre-defined content would be selected, while an input with pre-defined content wouldn’t be. That’s not ideal, I guess.

  5. Elements defined <!ELEMENT _ EMPTY> shouldn’t be selected and I’d add textarea to that to keep it in line with the other form elements.

    It seems to me that textarea is implicitly an empty form element. Given that though, putting textarea on the list of non-selected elements begs the question: should option elements also be included considering, like textarea, they also can only contain #PCDATA.

    Perhaps it’s just easier to only include elements defined EMPTY on the “do not select” list.

    Great question Eric.

  6. a) IIRC Mozilla also has known bugs with styling <br>, so it’s probably not a very good example. Form controls have a number of (mostly intended) styling restrictions as well, so you’d have to design your test case carefully for those.

    b) In CSS3 whether an element is a replaced element or not will depend on the value of its ‘content’ property. To avoid a circular dependency, then, a selector that differentiates between replaced and non-replaced content can’t be defined.

    Anyway, what would be your use case for a *:empty selector? I can’t think of any cases where you wouldn’t want a tag/class/id selector in there anyway.

  7. Styling for content that isn’t there is kind of counter-intuitive to me.
    Can I ask what the idea was that prompted this discovery?

  8. While the questioning is interesting, is perfectly understandable the fact that it selects img, input & other (by definition) empty elements, after all, they are empty.

    This may have no sense to you if you take the :empty statement outside of the DOM context, but at the same time is perfectly logical inside it (the body bug, of course, it’s just that: a bug).

    I do understand, however, your logic, but it reminds me of the problems measuring accessibility, which is subjective but measured by a computer. In this case I see it as the other way around: a computer tool for DOM manipulation (XPath) used in a subjective way :).

  9. I forgot to summarize…

    This is a problem of context. The :empty statement applies to empty elements. As far as I know it doesn’t take into consideration their attributes, and frankly I see no reason to do so, even more since you can reference them directly with [attribute].

  10. It seems like this is a bit open to interpretation. To me, empty means “no children”. So, if I want to find, say, all empty p elements, I could use p:empty for my selector. In my mind, it would make sense then for *:empty to apply to textarea elements if there is not text in them. If there is text, then they wouldn’t be selected.

    I also tend to agree with Ralph that the attributes wouldn’t be considered, therefore img elements would always be selected, as they have no child nodes.

    Perhaps a new pseudo-class is needed for the situation you bring up where an img element’s data successfully loads or not. Something like nocontent? I guess there would be some overlap between empty and something like that, though.

  11. Here’s another little weirdness in the spec:

    <object …></object> is matched.
    <object …><param name=”foo” value=”bar”/></object> is not.

  12. The DOM-Inspector tells me that the box you see is not body but the empty node from the head.
    Or am I missing something?

  13. Err, “the empty link-Node”.
    (Should have known that the blogsoft would filter it completely when written as tag)

  14. I’d actually expect that, Sebastian, since in the first case object has no child nodes, and in the second case it does. On the other hand, if the param isn’t selected, then that’s either total spec weirdness or a browser bug. To test it, you’d have to make sure the display of both object and param was something other than none.

    I think you are missing something, Nils. In the first part of the post, I’m talking about the body being inappropriately selected, as documented in the Bugzilla entry. In the second part, I’m talking about selection of empty elements that are DOM-empty but not render-empty, like images and inputs and such. I didn’t say anything about that appearance of the top-most tiny box on the test page, which is indeed the link element and (given that we accept HTML-empty elements are selected by :empty) should be seen, given the rest of the styles.

  15. Interesting… I wonder if we should see the link element. Why would head children be rendered at all? What sense does it have? I even find the br element not being shown (in the test page) has more sense, I mean, this is a replaced element, isn’t?. If that’s so, then most of CSS rules don’t apply at all —though I still wonder just what kind of element br is and how is it implemented across browsers.

    This leads me to another issue: should we be grateful with browser vendors that they give us some little freedom there (with replaced elements)? I mean, do you think this is positive at all? —If you ask me, I think it is, I tend to alter the border and background of input‘s, for instance. What’s your opinion?

  16. Ralph, the head children are being rendered because I opened up the head in order to display the embedded style element in the upper right corner there—and, by the by, to see if empty head descendants like link would be matched.

    As for allowing us to style replaced elements, I’m ambivalent, for many and sundry reasons.

  17. What happens to ?

    I agree that logic should rule what is affected and not.
    Meaning that elements that render content on the page are NOT :empty, such as images and :s.

    :empty elements are what the validator warns you about, I.E. elements that, possibly, contains only white-space nodes, comments or PI.
    Not what is referred to as EMPTY elements in the spec.

  18. I just found a name for my nascent web design business:

    head:empty

  19. :empty seems incompatible with html/xhtml, since whitespace is supposed to be ignored. For instance, if the html/xhtml spec says that:

    <p></p>
    and
    <p> [spaces] </p>

    are supposed to be equivalent, it seems to me that the browser could legitimately collapse the distinction b/t the two in the DOM model before applying the CSS styling. …Or I could be wrong on that – perhaps the spec says that one space is the same as twenty spaces, but any number of spaces is different then zero spaces…

  20. Would there be issue with any element that is self closing not being considered ’empty’? I see no benefit to targeting ‘br’ or ‘hr’ with :empty.

    As for ‘img’ and ‘input’, it seems to me that an attribute selector checking their ‘src’ and ‘value’ properties respectively would suffice when you wanted to see if they were truly ’empty’.

    This pseudo-class seems primarily targeted at paragraphs and divs without text, as I see little in the way of other practical uses for it (perhaps anchors?).

  21. I’ve just run into a situation where :empty not applying to input elements definitely seemed to go against common sense. I’m using javascript to find the first empty input element in a form and set focus to it. As Christopher mentioned, the attribute selector works just peachy instead–

    Here’s the selector string I’m using (with jQuery)

    $("[@value='']:input:visible:enabled:first")

    I agree that using on [@value=''] or [@src=''] makes more sense then the common-sense approach to :empty, although initially I was also a little thrown.

  22. So: you led me to playing around with this :empty stuff, eh? Just because I have a new job, and now I have to play catch-up… grrr….. ;-)

    Until now, I have completely ignored the :empty pseudo-element. A quick glance at the spec tells me that this might be a fairly handy feature for someone who has to populate a table with some dynamically-generated (tabular, of course) data, and wishes to highlight empty cells for whatever reason.

    So I did a quick-and-dirty experiment. (Granted, the contents aren’t dynamically generated, but for this experiment, it matters little.)

    Firefox 2.0.0.6/Mac handled this one with ease, albeit with a couple of minor rendering glitches; Safari 2.0.4 (419.3) blew it to pieces, styling every td as empty instead of just the empty ones. And the value of the very last cell, the number 12, is not within a p tag like the rest of the cells…

    Veddy intrestink.

  23. For the specific case of empty tables cells there’s a special rule already in CSS2.1: empty-cells:show|hide. Although, yes, :empty is more flexible.

Add Your Thoughts

Meyerweb dot com reserves the right to edit or remove any comment, especially when abusive or irrelevant to the topic at hand.

HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <em> <i> <q cite=""> <s> <strong> <pre class=""> <kbd>


if you’re satisfied with it.

Comment Preview