Reputation: 43843
I have this example
https://jsfiddle.net/kohnbhg3/
<div id="foo">
<div class="A">
A
</div>
<div class="B">
B
</div>
<div class="B">
C
</div>
<div class="B">
D
</div>
</div>
css
#foo .B {
margin:5px 0;
}
#foo .B:first-child {
margin:40px 0 5px 0;
}
#foo .B:last-child {
margin:5px 0 40px 0;
}
And I want the first child .B to have the margin top 40, and the last child .B to have margin bottom of 40, and in between margin 5 top and bottom.
However first-child seems to assume that the element must be the first child in the list overall. But right now it is only the first child that matched being .B.
Is there a css way of saying, first of the selected?
Does anyone know how I can fix this?
Thanks
Upvotes: 1
Views: 82
Reputation: 90068
You can apply different rules to the first match of multiple siblings (using CSS) by setting the rule for all siblings and overriding it for subsequent ones:
#foo > .B {
color: red;
}
#foo > .B ~ .B {
color: black;
}
<div id="foo">
<div class="A">
A
</div>
<div class="B">
B
</div>
<div class="B">
C
</div>
<div class="B">
D
</div>
</div>
However, this is not possible for the last match, as CSS only parses forwards, never backwards.
But you can use JavaScript for it. Here's how:
let parentSelector = '#foo', childSelector = '.B',
parentElement = document.querySelector(parentSelector),
firstChild = parentElement.querySelector(childSelector),
lastChild = [].slice.call(parentElement.querySelectorAll(childSelector)).slice(-1)[0];
Test:
let parentSelector = '#foo',
childSelector = '.B',
parentElement = document.querySelector(parentSelector),
firstChild = parentElement.querySelector(childSelector),
lastChild = [].slice.call(parentElement.querySelectorAll(childSelector)).slice(-1)[0];
firstChild.style.color = 'red';
lastChild.style.color = 'orange';
<div id="foo">
<div class="A">
A
</div>
<div class="B">
B
</div>
<div class="B">
C
</div>
<div class="B">
D
</div>
<div class="A">
E
</div>
</div>
You can take it further and apply classes to first and last elems instead of modifying styles on the fly, to increase maintainability, but that's a different subject altogether.
Keep in mind the above code only parses the first element matched by parentSelector
. If you have more than one and want to parse all, it gets a bit more complex:
let parentSelector = '.parent',
childSelector = '.B',
firstChild = (parent, childSelector) => parent.querySelector(childSelector),
lastChild = (parent, childSelector) => [].slice.call(parent.querySelectorAll(childSelector)).slice(-1)[0],
applyMyChanges = el => {
firstChild(el, childSelector).style.color = 'red';
lastChild(el, childSelector).style.color = 'orange'
};
[].slice.call(document.querySelectorAll(parentSelector)).map( applyMyChanges );
<div class="parent">
<div class="A">A</div>
<div class="B">B</div>
<div class="B">C</div>
<div class="B">D</div>
<div class="A">E</div>
</div>
<div class="parent">
<div class="A">A</div>
<div class="B">B</div>
<div class="B">C</div>
<div class="B">D</div>
<div class="A">E</div>
</div>
jQuery syntax for this task is a tad more concise:
For one instance (with #id
):
let parentSelector = '#foo',
childSelector = '.B';
$(childSelector, parentSelector).first().css({color:'red'});
$(childSelector, parentSelector).last().css({color:'orange'});
For any number of instances (with .class
):
let parentSelector = '.parent',
childSelector = '.B';
$(parentSelector).each(function() {
$(childSelector, this).first().css({color:'red'});
$(childSelector, this).last().css({color:'orange'});
});
jQuery test:
let parentSelector = '.parent',
childSelector = '.B';
$(parentSelector).each(function() {
$(childSelector, this).first().css({color:'red'});
$(childSelector, this).last().css({color:'orange'});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
<div class="A">A</div>
<div class="B">B</div>
<div class="B">C</div>
<div class="B">D</div>
<div class="A">E</div>
</div>
<div class="parent">
<div class="A">A</div>
<div class="B">B</div>
<div class="B">C</div>
<div class="B">D</div>
<div class="A">E</div>
</div>
Upvotes: 2
Reputation: 537
Look like you can not do this, unless all your element with class red
must be the same type in it's parent.
Your code will work if you use :first-of-type
for something like this:
<div id="foo">
<div class="A">
A
</div>
<p class="B">
B
</p>
<p class="B">
C
</p>
<p class="B">
D
</p>
</div>
There is a well explained for this problem here: CSS selector for first element with class
Hope you can get more info.
Upvotes: 0