How to select all elements between two elements?
Asked Answered
G

5

20

If I have this HTML, how can I select all elements between #one and #two with CSS? I can't use jQuery.

<p id="one"></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p id="two"></p>
Girardo answered 3/3, 2017 at 9:31 Comment(0)
D
15

The ~ general sibling combinator is what you are looking for

#one ~ p:not(#two) {
  color: red;
}

So what the above selector does is, it starts selecting all the p elements which are adjacent to #one till #two and later, I use :not() pseudo to exclude the last p:

#one ~ p:not(#two) {
    color: red;
}
<p id="one">0</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p id="two">6</p>

JSFiddle

Danieu answered 3/3, 2017 at 9:34 Comment(4)
In the jsfiddle example, if I add a <p>7</p> after <p id="two">6</p> this also becomes red.Girardo
Thanks, but that does not answer my question I'm afraid. I need a selector that only selects the p's between #one and #twoGirardo
@Girardo then either change your DOM structure, or you need to use JavaScriptDanieu
This doesn't answer the question being asked any more :P (also hi, you should join us in chat more)Hence
R
7

Given two elements that are siblings (i.e. have the same parent), it is not possible in Selectors 3 to write a selector that matches elements between them if the structure of elements is not known (or if it is known to change).

In Selectors 3, you can only match the elements between them if either or both of the following is true:

  1. The tree position of the two elements relative to their parent is known.

    For example, if #one is always the first child and #two is always the last child, this is easy enough:

    #one ~ p:not(#two)
    

    or

    #one ~ p:not(:last-child)
    

    (Note that I don't include :not(:first-child) because the ~ combinator already ensures that the first matching element is never the first child — you may want to include it for the additional specificity or even just for the sake of clarity.)

    If #one is always the second child and #two is always the fifth child:

    #one ~ p:not(:nth-child(-n+2)):not(:nth-child(n+5))
    

    If #one is always the second child and #two is always the third last child:

    #one ~ p:not(:nth-child(-n+2)):not(:nth-last-child(-n+3))
    
  2. The number of elements to match is known.

    Note that if #1 is true you should use the techniques described in #1, not here. The following technique is extremely tedious and is meant to be a last resort for when #1 is not true.

    If there will only ever be n elements between #one and #two, you will need to build a list of selectors beginning with #one, adding + p to each successive selector up to n times.

    For example, if there will only ever be three p elements between #one and #two:

    #one + p,
    #one + p + p,
    #one + p + p + p
    

In Selectors 4, assuming #one and #two are unique as in a conforming document, the following selectors are robust (i.e. they work for any structure of elements, even if it is known to change):

#one ~ p:has(~ #two)

or

#one ~ p:not(#two, #two ~ p)

Use :has() in jQuery and other non-CSS contexts where it is supported, and :not() in CSS (where :has() with the ~ combinator is not expected to be supported). The latter currently works in Safari; it is not yet known when it will ship in other browsers.

Ralli answered 4/3, 2017 at 6:30 Comment(2)
I was originally going to post this answer with Stack Snippets, but this bug makes it extremely difficult to do so. I'll do it later, when it's fixed. In the meantime, the examples should be easy enough to understand.Ralli
Tested all of the codeblocks here: 1 worked, 2 didn't work, 3 worked, 4 didn't work, 5 worked, 6 worked, 7 worked (as of: 28/05/2024). Hoping someone will fix this answer, as it's the correct solution - explicitly the :has() method is the ideal approach here.Phenazine
C
7
    #one ~ :not( #two ~ * ):not( #two )

Selects all element that are preceded of #one but not are preceded of #two and it is not #two

Pure logic, enjoy it

[UPDATE]

For the most browsers:

    :not(#one) {
      color: black /* color for out range */
    }

    #one~* {
      color: red;
    }

    #two~* , #two {
      color: black /* color for out range */
    }
Cacoepy answered 15/5, 2020 at 19:37 Comment(2)
Most browsers cannot run this selector. caniuse.com/css-not-sel-listLegwork
It work on most XML functionality with selector feature. I had update my answer if do you need a solve it for browserCacoepy
P
2

You can wrap the elements in a div element and assign the div an id that you will then use in the CSS file to select what is inside the tag.

<div id="ID">
    <p></p>
    <p></p>
    <p></p>
    <p></p>
    <p></p>
</div>

#id {
    color:red;
}
<p>1</p>
<div id="id">
    <p>2</p>
    <p>3</p>
    <p>4</p>
    <p>5</p>
</div>
<p>6</p>
Parse answered 3/3, 2017 at 9:36 Comment(0)
M
0

Yes, you actually can select elements between two elements:

.container {
  display: grid;
  grid-template-columns: 1fr;
}

.base {
  width: 100px;
  height: 50px;
  background: gray;
  border-radius: 8px;
  margin-bottom: 5px;
}

.first, .second {
  background: green;
}

.first ~ :not(.second):not(.second ~ *) {
    background: blue;
}
<div class='container'>
  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base first'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base second'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

  <div class='base'>
  </div>

</div>

https://codepen.io/Sariato-Jinks/pen/rNgdMbM

Morven answered 20/6, 2024 at 17:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.