What does the CSS selector :first-Child and :last-Child mean?

Asked

Viewed 2,651 times

11

I have a code using jQuery that was based on CSS selectors :first-child and :last-child (and that worked for a long time), to get the first input and the last input within a group containing input + select + input... The first input was the initial value of a range, and the last was the final value of the said interval, being that in the middle is the select with exclusion/inclusion operators extremity.

It was like this:

var $inputIni = $("input:first-child", controlDiv),
    $inputFim = $("input:last-child", controlDiv),
    $select = $("select", controlDiv);

jsfiddle

I came to the conclusion that the inputs did not have the clear purpose to the end user, and decided to put Labels for the same, as well as for the select from the middle. From there the dial :first-child didn’t work anymore. I can’t get to the elements anymore using the dial... because this?

jsfiddle

I imagine that the same problem, whatever it may be, may one day also happen with the selector :last-child, then how to understand the problem?

1 answer

18

Pseudo-classes do not work like this

It is common to make this confusion about the meaning of the selectors :first-child and :last-child, also called pseudo-classes, in addition to other pseudo-classes that denote position.

Pseudo-classes, when used in a selector, do not take into account the rest of the selector in question, hence the selector input:first-child does not refer to the first input, but yes to the input that is your father’s first child.

That means the pseudo-class :first-child will mark whatever the first element that is the child of any other. The same occurs with the pseudo-class :last-child that will mark only the last child.

Analogously, input:last-child means: the input that is the last child. Other pseudo-position classes also function analogously. Let’s see a list of their meanings:

Solutions to the problem

There are some solutions to the problem presented:

  • Using Ids for specific inputs instead of what is being done, maybe concatenating with the ID of the div that groups the elements, as indicated in jsfiddle, like this: <input name="ctl_ini" id="ctl_ini" /> and the selector so: $("#ctl_ini")

  • Using the pseudo-classes :first-of-type and :last-of-type thus: $("#ctl input:first-of-type") and $("#ctl input:last-of-type")

    jsfiddle

    Note that for use in CSS, support is somewhat limited. But in jQuery you can wear without worry.

A new confusion with the :first-of-type

The pseudo-classes :first-of-type and others based on the type of element, again cause a certain confusion, because apparently on the dial input:first-of-type the pseudo-class is based on the rest of the selector to get the result, which is not true.

Example:

  • a.cls:first-of-type: type element a, with the CSS class cls which is coincidentally the first of its kind.

    How we are selecting the type of the element: a, then the only "first kind" will be coincidentally of the type a, but that does not mean that the pseudo-class :first-of-type based on what came before on the selector. So much is true that it ignores the selector class: cls in its decision, that is, this selector doesn’t mean the first a with class cls.

Solution keeping :first-child and :last-child

As before you selected inputwho were his father’s first and last sons and now placed them within labels, bear in mind that now the labelare in the exact same place where the inputthey were, so they are label:first-child/select/label:last-child. With this in mind we can solve the situation by simply changing the selectors a little bit:

var fncDoSomethingWithCtl = function ($ctl) {
    
    var $inputIni = $("label:first-child input", $ctl),
        $inputFim = $("label:last-child input", $ctl),
        $select = $("select", $ctl);
    
    $inputIni.css({"border-color": "#C1E0FF", 
                   "border-width":"4px", 
                   "border-style":"solid"});
    
    $inputFim.css({"border-color": "#E0C1FF", 
                   "border-width":"4px", 
                   "border-style":"solid"});
    
    $select.css({"border-color": "#C1FFE0", 
                 "border-width":"4px", 
                 "border-style":"solid"});
    
}

$(function () {
    
    var $ctlDiv = $("#ctl");
    fncDoSomethingWithCtl($ctlDiv);
    
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="ctl">
    <label>
        <span>Início:</span>
        <input type="text"/>
    </label>
    <select>
        <option>inc até inc</option>
        <option>exc até inc</option>
        <option>inc até exc</option>
        <option>exc até exc</option>
    </select>
    <label>
        <span>Fim:</span>
        <input type="text" />
    </label>
</div>

Completion

The conclusion, is a security measure for the developer:

pseudo-classes should be treated at all times as mere coincidences... to know what the pseudo-class really means, just use it with nothing else on the same dial and then do an AND like this:

  • a:first-child will select the elements are selected both by selector a as by selector :first-child

So just test both selectors separately, and then merge the results and pay close attention to the levels of each element within a document, as in this case, label took the place of input and made him his son, where the input fell into the file hierarchy and turned :last-child of a label where the :first-child was a span.

Reference:

Information on pseudo-classes

  • 1

    Excellent clarification. I also discovered this not so long ago when I was implementing a java CSS selector based on the W3C specification. In my opinion these selectors are not intuitive if you do not read the documentation very carefully.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.