What does the CSS:first-child and:last-child selector mean?
I have code using jQuery that was based on CSS selectors :first-child
and
:last-child
(and that worked for quite a while), 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 said range,
being in the middle is the select
with delete/include operators
of the extremities.
Was like this:
var $inputIni = $("input:first-child", controlDiv),
$inputFim = $("input:last-child", controlDiv),
$select = $("select", controlDiv);
I came to the conclusion that the inputs did not have the clear purpose to
the end user, and I decided to put labels for them, as well as for the
select from the middle. From there the selector :first-child
no longer worked.
I can no longer get to the elements using the selector... why that?
I imagine that the same problem, whatever it is, may also one day happen with
the selector :last-child
, then as understand the problem?
1 answers
Pseudo-classes do not work like this
It is common to make this confusion about the meaning of selectors :first-child
and :last-child
, also called pseudo-classes, in addition to other pseudo-classes
which denote position.
Pseudo-classes, when used in a selector, do not take into account the rest
of the selector in question, therefore the selector input:first-child
does not refer to the
first input , but instead to the input that is the first child of its parent.
That means that 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
which will mark only the last child.
Similarly, input:last-child
means: the input that is the last child .
Other pseudo - classes of position also work analogously.
Let's look at a list with the respective meanings:
-
input:first-child
: input that is coincidentally the first son of the father -
input:last-child
: input that is coincidentally the last child of the parent -
input:nth-child(2)
: input that is coincidentally the second child of the parent -
input:nth-last-child(2)
: input that is coincidentally the penultimate child of the parent -
input:first-of-type
: input that is coincidentally the first of the type in the parent -
input:last-of-type
: input that is coincidentally the last of the type in the parent -
input:nth-of-type(2)
: input that is coincidentally the second of the type in the parent -
input:nth-last-of-type(2)
: input that is coincidentally the penultimate of the type in the parent input:only-child
: input that is coincidentally the first and last child of the parent (equalsinput:first-child:last-child
)input:only-of-type
: input that is coincidentally the first and last child of the type in the parent (equalsinput:first-of-type:last-of-type
)
Solutions to the problem
There are some solutions to the problem presented:
Using IDs for specific inputs rather than what is being done, perhaps concatenating with the ID of the Div that groups the elements, as indicated in the jsfiddle, like this:
<input name="ctl_ini" id="ctl_ini" />
and the selector like this:$("#ctl_ini")
-
Use the pseudo-classes
:first-of-type
and:last-of-type
like so:$("#ctl input:first-of-type")
and$("#ctl input:last-of-type")
Note that for use in CSS, support is a bit limited. But in jQuery can use without worry.
A new confusion with :first-of-type
The pseudo-classes :first-of-type
and others that are based on the type of the
element, again cause a certain confusion, because
apparently in the selector input:first-of-type
the pseudo-class is
relying on the rest of the selector to get the result, which is not true.
Example:
-
a.cls:first-of-type
: element of Typea
, with CSS classcls
which is coincidentally the first of its kind.Since we are selecting the type of the element:
a
, then the only "first of the type" will be coincidentally of Typea
, but that's not to say that the pseudo-class:first-of-type
was based on what came before in the selector. So much is true that it ignores the selector class:cls
in its decision, i.e. that selector does not mean the firsta
with the classcls
.
Solution keeping :first-child
and :last-child
As before you selected the input
s that were first and last Children of your father and now put them within label
s, keep in mind that now the label
s are in the exact same place where the input
s 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:
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>
Conclusion
The conclusion, is a security measure for the developer:
Pseudo-classes should always be treated as mere coincidences... to know what the pseudo-class really means, just use it without anything else in the same selector and then do an AND like this:
-
a:first-child
will select the elements are selected both by selectora
as by selector:first-child
Then 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 it its child, where input
fell into the file hierarchy and turned :last-child
from a label
where :first-child
was a span
.