Last Comment Bug 418039 - Implement the :has() pseudo class
: Implement the :has() pseudo class
Status: NEW
: dev-doc-needed
Product: Core
Classification: Components
Component: CSS Parsing and Computation (show other bugs)
: unspecified
: All All
: -- enhancement with 27 votes (vote)
: ---
Assigned To: Nobody; OK to take it and work on it
:
: Jet Villegas (:jet)
Mentors:
http://dev.w3.org/csswg/selectors4/#h...
Depends on:
Blocks: selectors4 1169858
  Show dependency treegraph
 
Reported: 2008-02-16 22:10 PST by Josh Triplett
Modified: 2016-05-18 16:47 PDT (History)
16 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments

Description Josh Triplett 2008-02-16 22:10:10 PST
For numerous reasons, I would really like to have has-child and has-descendant selectors in CSS.  These selectors would select an element which has an immediate child matching a selector or a descendant matching a selector, respectively.

To avoid conflicting with any official CSS feature, this should most likely use CSS extension syntax, which would make the selectors -moz-has-child and -moz-has-descendant.

These selectors would allow rules such as:

/* Put a red border around forms that have a field with class="error". */
fieldset:-moz-has-child(.error) { border: 1px solid red; }

/* Show a message asking the user to mark at least one checkbox from
   the form. */
form.choices:-moz-has-descendant(*:checked) div.warning { display: none; }

/* Highlight the section containing the current anchor. */
div:-moz-has-descendant(*:target) { background-color: yellow; }

/* Put a page break before tables which have at least 20 rows, putting
   them at the top of their own page. */
table:-moz-has-child(tr:nth-of-type(20)) { page-break-before: always; }

/* Put a border between sections, treating divs as sections if they
   contain an h2 as their first child. */
div:-moz-has-child(h2:first-child) { border-bottom: 1px solid black; }

/* Picture the following as an Adblock Plus rule; it could easily
   replace many of the heuristics currently used to block the
   containing elements of ads, such as matching divs with a given
   width or style. */
div:-moz-has-child(img[href^="http://adserver.example.org"]) { display: none; }


I think these examples show some of the promise of these selectors.
Comment 1 David Baron :dbaron: ⌚️UTC-8 2008-02-17 11:37:32 PST
These proposals look more like the :matches() and :has() proposals than the :subject proposal that was in early drafts of css3-selectors.  That said, they're all extremely inefficient to implement.  The danger of implementing them is that authors might use them, and then either authors or users will complain about how (unavoidably) slow they are.
Comment 2 David Baron :dbaron: ⌚️UTC-8 2008-02-17 11:40:25 PST
Existing proposals:
http://www.w3.org/TR/2000/WD-css3-selectors-20001005/#subject-pseudo
http://lists.w3.org/Archives/Public/www-style/2002May/0037.html

There may be a duplicate bug as well.
Comment 3 Josh Triplett 2008-02-17 13:26:20 PST
Looking at that proposal, I like :subject much better than the approach I've suggested.  :subject avoids introducing new syntax which lacks symmetry with the existing selectors.  All but one of the examples I gave seems to work with the :subject selector:

/* Put a red border around forms that have a field with class="error". */
fieldset:subject > .error { border: 1px solid red; }

/* Highlight the section containing the current anchor. */
div:subject *:target { background-color: yellow; }

/* Put a page break before tables which have at least 20 rows, putting
   them at the top of their own page. */
table:subject tr:nth-of-type(20) { page-break-before: always; }

/* Put a border between sections, treating divs as sections if they
   contain an h2 as their first child. */
div:subject h2:first-child { border-bottom: 1px solid black; }

/* Picture the following as an Adblock Plus rule; it could easily
   replace many of the heuristics currently used to block the
   containing elements of ads, such as matching divs with a given
   width or style. */
div:subject img[href^="http://adserver.example.org"] { display: none; }


The one example that doesn't work:

/* Show a message asking the user to mark at least one checkbox from
   the form. */
form.choices:-moz-has-descendant(*:checked) div.warning { display: none; }

That one requires applying subject and then applying further CSS combinators starting from the element chosen by :subject:

(form.choices:subject *:checked) div.warning

While useful, I can definitely live without that, and it might prove inefficient as well as complex to implement.

However, the :subject selector itself does not seem fundamentally less efficient than other CSS selectors or combinators.  It does seem harder to implement, but not computationally harder.  The main difficulty seems more likely to come from the need to process child elements before rendering the parent.  However, in terms of computation, what if Gecko simply matched the CSS rule precisely as it would without :subject, and then *once it found a matching element* it went up to the :subject element and applied the style?  That seems no less efficient than existing CSS rules.
Comment 4 David Baron :dbaron: ⌚️UTC-8 2008-02-17 14:28:39 PST
It is computationally harder.  Just matching as though :subject weren't there is far from trivial (if possible at all), for a number of reasons:
 * "div:subject p" can match many divs for a single p
 * "div:subject :link:hover" mean we need to recompute style for the entire document when somebody changes whether they're hovering over a link.

That said, by caching a bunch of things in various places and being very careful about what caches to invalidate when, it might be possible.
Comment 5 David Baron :dbaron: ⌚️UTC-8 2008-02-17 14:31:54 PST
Er, what I meant is that any reasonably simple way of implementing it is computationally harder.  However, there might be some tricks we could use to avoid that, but they're far from trivial, if possible at all.
Comment 6 David Baron :dbaron: ⌚️UTC-8 2008-02-17 14:52:41 PST
Actually, if we cache on each node (or maybe even on the style context -- that might be a little trickier) the list of nodes matching rules that would normally have it as a subject but instead had a :subject, it probably wouldn't be that bad, although processing the reresolution process would be tricky.  We'd need to build lists of what needed rules changed outside the tree we're reresolving style on -- and then we'd need something like ReParentStyleContext to do a bunch of rebuilding.  Except to make this efficient we'd probably want to do something like cache an mNextRuleNode on style contexts -- along with caching both old and new lists of the external rules.  Or something roughly like that.
Comment 7 David Baron :dbaron: ⌚️UTC-8 2008-05-14 22:12:12 PDT
Caching on the style context would be tricky because of the FindChildWithRules optimization.
Comment 8 David Baron :dbaron: ⌚️UTC-8 2009-03-24 18:41:37 PDT
The other things that would be bad are:
 * potentially messy re-resolution as we matched rules on descendants (for style contexts that we'd already resolved)

 * having to do style resolution inside display:none subtrees just to match things whose :subject is outside the subtree
Comment 9 spammed.user 2012-02-09 07:31:42 PST
see http://www.w3.org/TR/selectors4/#subject
Comment 10 sjw 2014-11-29 11:04:43 PST
The current editors draft proposes :has().

Note You need to log in before you can comment on or make changes to this bug.