W3C home > Mailing lists > Public > www-style@w3.org > February 2015

Re: [selectors] Proposal: :n-children() selector

From: Matt Mastracci <matthew@mastracci.com>
Date: Sun, 1 Feb 2015 12:33:15 -0700
Cc: www-style list <www-style@w3.org>
Message-Id: <8186810D-3606-4120-BEEF-BD33EDA15AEE@mastracci.com>
To: Lea Verou <lea@verou.me>
(unfortunately I missed this reply earlier as my message ended up getting posted multiple times, which caused a bunch of confusion off-list ó sorry!). 

I wrote the original message before becoming aware of :has in the new spec so I havenít had a chance to compare the two.

:n-children() isnít an perfect analogue to :has() or the subject indicator ó it is less powerful, and correspondingly less expensive to implement. 

With :has(Ö), you can select down many levels below the parent. In the case of :n-children(), a rule can only examine the direct children of the parent for matches. This is roughly akin to > vs >>>. div:has(> img) has an analogue with :n-children(), while div:has(img) has no corresponding equivalent.

The ďof SĒ portion of the selector is cribbed from the new nth-child(an+b of S) syntax. While it could be omitted, I believe it wouldnít add further complexity to the fast selector spec as this feature would be landing in nth-child/nth-last-child, replacing the :nth-match() selector from earlier versions of the spec. 

:n-siblings() is effectively equivalent to :nth-child():nth-last-child() plus the sibling selector. :n-children() is a novel construction, but simply does the lookup from the direct parent of those siblings which (again, I believe) is approximately equivalent in complexity to :n-siblings(). While it doesnít offer the full flexibility of :has, I believe it hoists a number of useful use cases into a selector that can be made performant.

Matt.

> On Jan 22, 2015, at 8:20 PM, Lea Verou <lea@verou.me> wrote:
> 
> This is basically equivalent to the parent selector / subject indicator, so it brings all the problems associated with it.
> For example, .foo:n-children(n + 1 of .bar) is essentially equivalent to !.foo > .bar or .foo:has(.bar).
> Therefore, even if itís accepted, I doubt itís feasible to add in the fast profile, and the complete profile is useless syntactic sugar at the moment.
> It *might* be feasible to add just the :n-children(an+b) syntax (without the selector), since we already have :empty, but someone else needs to confirm. If itís possible, I think it would be a very useful addition. 
> 
> On the other hand, something like :n-siblings() would be useful and not too hard to add, as itís basically syntactic sugar over existing selectors.
> 
> ~Lea
> 
> On Jan 13, 2015, at 18:56, Matt Mastracci <matthew@mastracci.com> wrote:
> 
>> I'd like to propose addition of functionality to CSS selectors level 4 to style an element conditionally based on the number of direct descendants it has. 
>> 
>> Note that this is not entirely new functionality as you can emulate it in some cases by bending the current selectors around a bit. [1][2][3]
>> 
>> Justification
>> =============
>> 
>> This is useful building block for responsive, script-free structures on a page that dynamically adjust to the number of elements they contain [4][9]. See the "example use case" section below for specific examples where it would allow novel behaviour or simplify an existing case.
>> 
>> As mentioned before, this is not entirely new functionality. The case where one styles children based on the number of elements directly contained by their parent can be achieved today by specifying both :nth-child and :nth-last-child on a child and the general sibling selector (~). [1][2][3] More complicated cases (e.g.: the fourth, seventh, tenth, etc. child of n, where n+2 is a multiple of three) can be difficult to reason through and not easy to parse when found in an existing stylesheet.
>> 
>> What this selector does is make this existing functionality easier to read and write, while also opening up additional possibilities to style an element based on the number of children it has. This selector is to :empty and :not(:empty) as :nth-child/:last-child is to :first-child/:last-child.
>> 
>> This selector adds a new case where an element's style may change as an HTML document is streamed from a remote server. I believe there is a precedent for this in the existing :nth-last-child() and :empty() selectors which may change state as the document is loaded, and the browser should use the same strategy for the new selector.
>> 
>> Proposed syntax
>> ===============
>> 
>> The proposed new selector looks and acts as similar to the existing :nth-child() selector as possible:
>> 
>> :n-children(An+B [of S?])
>> 
>> An+B [5] represents a formula determining the number of children in an element. "even" and "odd" are also valid in its place.
>> 
>> The behaviour of "of S" is analogous to its definition for :nth-child in CSS Selectors Level 4.
>> 
>> For a developer who understands nth-child, the result of the examples here should be unsurprising and for the most part, analogous to their nth-child counterpart:
>> 
>> * :n-children(2) 
>>     matches an element with exactly two children, of any type
>> 
>> * :n-children(2 of div.important) 
>>     matches an element with exactly two elements that match div.important (other elements are not considered)
>> 
>> * :n-children(even) 
>>     matches an element with an even number of children (including zero)
>> 
>> * :n-children(10n-1) 
>>     matches an element with 9, 19, 29, etc children
>> 
>> * :n-children(10n+9) 
>>     matches an element with 9, 19, 29, etc children
>> 
>> * :n-children(10n+-1) 
>>     syntactically invalid, and would be ignored
>> 
>> * :n-children(n+1) 
>>     matches an element with 1 or more children (equivalent to :not(:empty))
>> 
>> * :n-children(n+2) 
>>     matches an element with 2 or more children
>> 
>> * :n-children(-n+2) 
>>     matches an element with 2 or fewer children
>> 
>> Example use cases
>> =================
>> 
>> This particular selector is not entirely novel. These are real-world use cases that would benefit from the addition of an n-children() selector:
>> 
>> * Responsive image galleries that adjust cleanly to the number of elements [4]
>> * Shrinking the height of dynamically added elements as more are added [6]
>> * Turning on "overflow" UI when reaching a certain number of children (see example below)
>> * Simplified table sizing for small numbers of known cases [7] 
>> * Responsive/auto-sizing widget layouts [8]
>> * List as a fully-justified grid [9]
>> 
>> Example CSS
>> ===========
>> 
>> A container that shows/hides an "overflow" toggle button if five or more .child items are contained within:
>> 
>>    div.container button.overflow {
>>        display: none;
>>    }
>> 
>>    div.container:n-children(n+5 of .child) button.overflow {
>>        display: block !important;
>>    }
>> 
>>    div.container:not(.expanded) .child:nth-child(n+5 of .child) {
>>        display: none;
>>    }
>> 
>> Auto-sizing, fully-justified sidebar widgets (half-sized in pairs, first widget full-sized if there is an odd number):
>> 
>>    div.sidebar .widget {
>>        width: 50px;
>>        height: 50px;
>>        display: inline-block;
>>    }
>> 
>>    div.sidebar:n-children(odd of .widget) .widget:first-of-type {
>>        width: 100px;
>>        height: 100px;
>>    }
>> 
>> Alternatives
>> ============
>> 
>> 1. Rather than adding a selector to the parent, we can add a selector to an element that would select based on the number of siblings (i.e.: :n-siblings()). This has the advantage of being similar enough to existing code that the impact on CSS engines would be less than the full proposal.
>> 
>> 2. Do nothing (if the case where styling a parent based on the number of children is not deemed important enough to add this functionality to the spec). For users wishing to style children based on the number of siblings, they can continue to use both :nth-child and :nth-last-child to do this.
>> 
>> References:
>> 
>> [1] http://grack.com/blog/2015/01/09/abusing-css3-selectors
>> [2] http://lea.verou.me/2011/01/styling-children-based-on-their-number-with-css3/
>> [3] http://andr3.net/blog/post/142
>> [4] http://grack.com/assets/2015/01/nth-child/image-count.html
>> [5] http://dev.w3.org/csswg/css-syntax/#anb
>> [6] http://bytesizematters.com/
>> [7] https://plus.google.com/100749189589213497870/posts/dgz5imd3jDE
>> [8] https://wordpress.org/support/topic/media-query-breakpoints
>> [9] http://codepen.io/heydon/pen/bcdrl
>> 
>> 
> 
Received on Sunday, 1 February 2015 19:33:46 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 22:52:01 UTC