Do you want to distribute elements, tied to their number, on the same styles? Yes easily

    An alternative article title is “almost: child-count (n)”. Because that’s how it all works. On bare CSS and without any data attributes or anything else in the layout.

    Imagine you have, for example, some kind of news feed. It doesn't matter which one. The main thing is that you do not know how many elements will be in it, and how to arrange them so that it is symmetrical. And I want to do something useless, but beautiful: for example, arrange everything in two columns, and insert some blocks in full width. Every third, or every fifth.

    Of course, if you have four elements, and the third you made in full width - the last will hang in the end. Therefore, you need to apply such a beautiful and useless thing only if the number of elements is a multiple of three. And if there is an odd number of them (but not a multiple of three) - you need to do, for example, the last element in the entire busbar.
    Here it is, for example:





    Or, for example, you decided to make a radio, as in GTA 5. Here it is:

    image

    And you want to arrange the elements in a circle, but you do not know how many there are. Of course, for different cases - you need to specify a different transform: rotate (). Well, or, if desired, you can arrange everything through left and top, relying on sin and cos. But still, you need to know how many elements you have.

    In a project where it was necessary to implement just such a feature, I first did everything through the simplest js:

    function countElementChildren(element) {
        $(element).attr('children', $(element).children().length)
    }
    


    And in scss I worked directly with

    @for $i from 1 through 20 {
        .parent[children="#{$i}"] {
            @for $j from 1 through $i {
                 & > :nth-child(#{$j}) {
                     transform: rotate(360deg * (($j - 1) / ($i - 1)));
                 }
            }
        }
    }
    


    I hope this code is clear to everyone. If not, it unfolds in a construction of the form .parent [children = 2]>: nth-child (0) {transform: rotate (0deg)} and further in the loop

    You could wrap this function call in MutationObserver to monitor the number of children, but still there was a sense of the incorrectness of this decision - it was just a style that did not relate to the main logic.

    Probably, every person who has been practicing styles for quite some time, somewhere at the level of intuition, a detector appears "can this be done on the same styles."
    As a result, almost a day in the background was a search for a solution to how to determine the number of elements. There were even two solutions, though very similar.
    The logic was quite simple, technically we can refer to almost any element forward (through ~ and +), but to determine the number of elements in the list, you need to be able to go forward, count all the elements and go back.
    The "reverse" movement is possible with just two attributes - nth-last-of-type and nth-last-child.
    Moreover, in fact, this movement is performed from the last element, so in the end you need to go through all the elements starting from the first and ending with the last through nth-last-child and discover the "finality" of the element through first-child.
    As a result, the first version of the selector has become

    :nth-last-child(20):first-child {
    }
    


    which made it possible to refer to the first element if it is the 20th from the end (that is, its parent has only 20 children).
    You could go to any next item like this:

    :nth-last-child(20):first-child ~ :nth-child(10) {
    }
    


    It gave readability, but it still looked somewhat bulky.
    It was time to remember that: first-child is a special case: nth-child (1), in the end we got a selector

    :nth-child(1):nth-last-child(20) {
    }
    


    That made it possible to move left and right by any means, clearly "clinging" to the beginning and end of the list of children.

    In real scss, it turned into a somewhat monstrous construction

    @for $i from 1 through 20 {
        @for $j from 1 through $i {
             .child:nth-child(#{$j}):nth-last-child(#{$i - $j}) {
                 @include transform(rotate(360deg * (($j - 1) / ($i - 1))))
             }
        }
    }
    


    Here are examples of how this works:
    An example with different widths of blocks depending on their number
    An example with a uniform distribution of elements in a circle

    This example is perhaps a little contrived, but in reality they show the real possibilities of such a "point" conditional access to elements.

    Of the minuses, I could name only the large size of the output css file: for a design of 20 elements, each selector of which had 14 lines due to vendor prefixes, the length of the resulting style was about 2000 lines.

    I do not know how else this can be used, but there is clearly something in it, something big and interesting. If you have ideas, I will gladly add examples here, at the end of the post.

    Also popular now: