w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.51k stars 671 forks source link

[selectors-4] Add a count to :has() #1547

Open srambach opened 7 years ago

srambach commented 7 years ago

The relational pseudo-class :has() is proposed to take a relative selector list as an argument. https://drafts.csswg.org/selectors-4/#relational

It would be very useful to be able to know how many children (and/or children of a type) the element has. For example, you may want to style a menu differently if it has more than n elements.

Currently, you can use a combination of :nth-last-child, and :first-child to determine at the child level how many siblings there are. (described very well in https://css-tricks.com/extremely-handy-nth-child-recipes-sass-mixins/)

However, this doesn't allow styling of the parent based on the number of children. Presumably it would be desirable to be able to select based on >, <, or = n children.

Loirooriol commented 7 years ago

Also see #1176

ewilligers commented 5 years ago

However, this doesn't allow styling of the parent based on the number of children.

Note that :has cannot be used in style rules. It can be used by script:

const parents = document.querySelectorAll('a:has(> img)');
for (let parent of parents) {
  const numChildren = parent.querySelectorAll('* > img').length;
  if (numChildren > ...)
    parent.style. ... = ...;
  else
    ...
}
tomhodgins commented 5 years ago

You might want to check out quantity queries which are kind of a selector hack that targets an element based on the number of siblings :D It's functionally similar to what you're asking for and supported in browsers right now, though it is a little ugly to look at and reason with.

Going the JS route, I've implemented responsive conditions like min-characters, characters, and max-characters as element queries a couple of different ways that we can use in the meantime while we wait for browsers to support :has(). You can use EQCSS as a custom syntax like this with (min-children: n) or (children: n) or (max-children: n):

<ul>
  <li>one
</ul>
<ul>
  <li>one
  <li>two
</ul>
<ul>
  <li>one
  <li>two
  <li>three
</ul>

<style>
  @element ul and (children: 2) {
    :self {
      background: lime;
    }
  }
</style>

<script src=https://elementqueries.com/EQCSS.js></script>

as well as in CSS, parsed from CSSOM and run through javascript plugins like this:

<ul>
  <li>one
</ul>
<ul>
  <li>one
  <li>two
</ul>
<ul>
  <li>one
  <li>two
  <li>three
</ul>

<style>
  @supports (--element("ul", {"children": 2})) {
    [--self] {
      background: lime;
    }
  }
</style>

<script type=module>
  import deqaf from 'https://unpkg.com/deqaf/index.js'
  import element from 'https://unpkg.com/jsincss-element-query/index.vanilla.js'

  deqaf({stylesheet: {element}})
</script>

And I've also written a plugin for supporting the :has() selector which would work too, if you wanted to target the <ul> in the way that :has() would using a quantity query, rather than by targeting the <li> themselves as the quantity query selector would do:

<ul>
  <li>one
</ul>
<ul>
  <li>one
  <li>two
</ul>
<ul>
  <li>one
  <li>two
  <li>three
</ul>

<style>
  ul[--has='"li:nth-last-child(n+2):nth-last-child(-n+2):first-child, li:nth-last-child(n+2):nth-last-child(-n+2):first-child ~ li"'] {
    background: lime;
  }
</style>

<script type=module>
  import deqaf from 'https://unpkg.com/deqaf/index.js'
  import has from 'https://unpkg.com/jsincss-has-selector/index.vanilla.js'

  deqaf({rule: {has}})
</script>
bramus commented 2 years ago

Allow styling of the parent based on the number of children.

This is possible nowadays, thanks to :has():

/* Less than 3 children */
ul:has(> :nth-child(-n+2):last-child) {
    border: 1px solid red;
}

/* Exactly 5 children */
ul:has(> :nth-child(5):last-child) {
    border: 1px solid blue;
}

/* More than 7 children */
ul:has(> :nth-child(7)) {
    border: 1px solid green;
}

Try it out in your browser if it has :has() support: https://codepen.io/bramus/pen/bGKoZNq?editors=0110

I guess this issue may be closed?

Loirooriol commented 2 years ago

For "Less than 3 children", you probably want to include 0 children, so ul:not(:has(> :nth-child(3))). Also see reductions for An+B of S cases in https://github.com/w3c/csswg-drafts/issues/5694#issuecomment-722736765