Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select last sibling in each group of siblings

So I want to select the last sibling in a group of elements, but there are more than one group in the parent container.

<div class="wrapper">

  <div class="select"></div><!-- padding-top -->
  <div class="select"></div>
  <div class="select"></div><!-- padding-bottom -->

  <p>Some text</p>

  <div class="select"></div><!-- padding-top -->
  <div class="select"></div>
  <div class="select"></div>
  <div class="select"></div>
  <div class="select"></div><!-- padding-bottom -->

<div>

So in this case, for every clump of .select elements, I need to add padding-top to the first one and padding-bottom to the last one. The tricky part is that the clump of .select elements can be infinitely large/small, but the first and last elements need padding.

This example works for the top padding...

// add padding-top to all elements
.select {
  padding-top: 1em;
}

// remove it for general direct siblings
.select + .select {
  padding-top: 0;
}

// this will only add to the last .select in the parent container, 
// not the last in each clump of .select elements
.select + .select:last-of-type {
  padding-bottom: 1em;
}

But the bottom padding for the last element in the clump is what I'm having issues with.

I can't add wrapper elements because this is pseudo generated code. JS would also make this a fairly straightforward solution, but I'd rather exhaust all of my CSS only solutions first.

Edit: The HTML structure is 100% arbitrary as it will be generated by the user in a WYSIWYG. However, there will be some sections of .select elements next to each other.

like image 515
Daron Spence Avatar asked Dec 18 '15 20:12

Daron Spence


People also ask

Is there a previous sibling selector?

No, there is no "previous sibling" selector. On a related note, ~ is for general successor sibling (meaning the element comes after this one, but not necessarily immediately after) and is a CSS3 selector. + is for next sibling and is CSS2. 1.

How do I select a specific sibling in CSS?

Adjacent Sibling Selector (+) The adjacent sibling selector is used to select an element that is directly after another specific element. Sibling elements must have the same parent element, and "adjacent" means "immediately following".

What is general sibling selector?

The general sibling selector (~) selects all elements that are next siblings of a specified element.


2 Answers

.wrapper>*:not(.select) + .select, .wrapper>.select + *:not(.select) {
    margin-top: 1em;
}
.wrapper>.select:last-child {
    margin-bottom: 1em;
}

If positioning was not the issue, and setting a margin-top on the next element doesn't do it (for example you have a background on .select and you actually need bottom padding on the last item in the group), well... you can't select the previous sibling with CSS.

So here's a solution using jQuery:

$('.wrapper>*:not(.select)').each(function() {
  $(this).prev(".select").css({
    'padding-bottom': '20px'
  })
})
body {
  margin: 0;
}
.wrapper {
  padding: 20px;
}
.select {
  background-color: #eee;
  border-bottom: 1px solid white;
}
/* ignore CSS above, it's for coloring and padding so you see elements */

.select {
  padding-top: 20px;
}
.select ~ .select {
  padding-top: 0;
}
*:not(.select) + .select {
  padding-top: 20px;
}
.wrapper>.select:last-child {
  padding-bottom: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
  <div class="select">select</div>
  <!-- padding-top -->
  <div class="select">select</div>
  <div class="select">select</div>
  <!-- padding-bottom -->

  <p>Some text</p>

  <div class="select">select</div>
  <!-- padding-top -->
  <div class="select">select</div>
  <div class="select">select</div>
  <div class="select">select</div>
  <div class="select">select</div>
  <!-- padding-bottom -->

</div>
like image 129
tao Avatar answered Sep 23 '22 05:09

tao


Old question, I know, but I just had the same problem so thought this might be helpful for someone. Using :not() worked for me; I used borders instead of paddings for the example so it's more visible.

.wrapper * {
    margin: 0;
    padding: 0;
}
.select {
    border-top: 30px solid #f00;
}
.select:last-of-type {
    border-bottom: 30px solid #f00;
}
.select + .select {
    border-top: 0;
}
.select + :not(.select) {
    border-top: 30px solid #f00;
}
<div class="wrapper">
    
    <div class="select">Select 1</div><!-- padding-top -->
    <div class="select">Select 2</div>
    <div class="select">Select 3</div><!-- padding-bottom -->

    <p>Some text</p>

    <div class="select">Select 4</div><!-- padding-top -->
    <div class="select">Select 5</div>
    <div class="select">Select 6</div>
    <div class="select">Select 7</div>
    <div class="select">Select 8</div><!-- padding-bottom -->
    
<div>
like image 31
Niffler Avatar answered Sep 25 '22 05:09

Niffler