0

This one is puzzling me because it seems so simple and yet I can't solve the problem.

To give a bit of context, I'm preparing a little HTML application that generates a list of divs based on the filters chosen by the end user. The end user can then manually remove elements he's not interested to, basically by adding the class "hidden" to them via JavaScript.

The problem is, some of these elements have a bigger border and when they become adjacent on the screen the border becomes even thicker, and thus uglier. I managed to solve all other problems, but now when I have a situation similar to this, I can't find the correct CSS selector:

<div>...</div>
<div>...</div>
<div class="bigborders">...</div>
<div class="hidden">...</div>
<div class="hidden">...</div>
<div class="hidden">...</div>
<div class="bigborders">...</div>
<div>...</div>
<div class="bigborders">...</div>
<div>...</div>
<div>...</div>

And for the sake of it, let's add some CSS rules, simplified to the bone:

.hidden {
    display: none;
}
.bigborders {
    border-top: 2px solid #AAAAAA;
    border-bottom: 2px solid #AAAAAA;
}

https://jsfiddle.net/3tzrsq1d/1/

I'd like to select only the first .bigborders after another .bigborders, ignoring all the hidden divs in between, to remove its upper border, but the only selectors for siblings are +, that selects the element immediately following (but that's true only on the screen, not in the code), and ~, that selects every following element (but I need only the first one, not every one). I also experimented with :first-of-type and nth-of-type, but this pseudo-class works with respect to the parent element of the first .bigborders, not that element itself.

Is there another way that I can't see?

GRB
  • 425
  • 1
  • 6
  • 20
  • javascript allowed ? – Iman Emadi Apr 10 '20 at 22:05
  • @BrightFaith I would have no problem solving this situation with JS, but I'm purposefully trying to avoid it if there is a CSS alternative. Otherwise the JS code is already written, I just have to uncomment it. – GRB Apr 10 '20 at 22:11
  • @GRB did you try [nth of type](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type)? – Rachel Gallen Apr 10 '20 at 22:20
  • @BrightFaith I suspected as much myself, but I still hope that there is some strange approach to the problem I haven't considered. Right now I'm thinking of replacing upper and lower borders with colored divs, maybe I can manage to have them overlap in an easier way. – GRB Apr 10 '20 at 22:22
  • @RachelGallen Yes, but it shares the same problem as first-of-type, it's counted with respect to the parent element. – GRB Apr 10 '20 at 22:24
  • @GRB please post a [mcve] with HTML and CSS. – zer00ne Apr 10 '20 at 22:33
  • AFAIK, CSS currently doesn't have a pseudo-selector that you can use for that purpose, because of how they work. You will need to use JS to do the selection, unfortunately. – Terry Apr 10 '20 at 22:34
  • 1
    @zer00ne Working on it, sorry. – GRB Apr 10 '20 at 22:37
  • 1
    @GRB I think what you need is to investigate [find closest](https://stackoverflow.com/questions/22119673/find-the-closest-ancestor-element-that-has-a-specific-class) Some more examples are given [here](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) – Rachel Gallen Apr 10 '20 at 22:38
  • @RachelGallen The problem can be solved in an instant using JS, I used jQuery for example (but your suggestion seems much more elegant than mine). But experience has thought me that a CSS solution is always preferable, if it exist. It seems it doesn't, so I will probably resort to JS in the end. – GRB Apr 10 '20 at 22:45
  • 1
    @GRB the link I posted contains vanilla js, aswell as jquery solutions. ( also - it's 'taught', not 'thought' - sorry i'm a language pedant... ). Good luck with it. – Rachel Gallen Apr 10 '20 at 22:50
  • @RachelGallen No need to feel sorry, it's a chance to improve my English. ;) – GRB Apr 10 '20 at 23:01

3 Answers3

1

Overly complicated the first solution.

First apply top border to all elements,

 border-top: 1px solid;

Now only the last item lack bottom border we can fix this use :last-child

Second apply the relevant borders to .bigBorders elements.

border-top: 3px solid blue;
border-bottom: 2px solid blue;

Why 2px on the bottom ? Because other elements have 1px top border the sum will be 3px which will equal the top border of .bigBorders.

This is all to make all borders even.

Now we apply margin-bottom: -3px; on .bigBorders this will only affect the elements on the bottom, it's like we're decreasing the height of the element above them.

3px equals to the border width of the element above which will make the borders overlap.

And yes the margin trick will effect content, but as long as the values are small it's should be fine.

.hidden {
    display: none;
}


div>div {
    border-top: 1px solid;
    padding: 5px;
}

div>div:last-child {
    border-bottom: 1px solid;
    
}

.bigborders {
    border-top: 3px solid;
    border-bottom: 2px solid;
    margin-bottom: -3px;
    background: orange;
}
<div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="bigborders">bigborders</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="bigborders">bigborders</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="bigborders">bigborders </div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders </div>
  <div class="bigborders">bigborders </div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders </div>
  <div class="bigborders">bigborders </div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders </div>
  <div class="bigborders">bigborders </div>
  <div>placeholder</div>
</div>

Or we can also simplify it like this if you don't care much about even widths yet you can still use :last-child :first-child to target first and last child and adjust their borders individually.

.hidden {
  display: none;
}

div>div {
  border: 1px solid;
  padding: 5px;
}

.bigborders {
  border-top: 3px solid;
  border-bottom: 4px solid;
  margin-bottom: -3px;
  background: orange;
}
<div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="bigborders">bigborders</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="bigborders">bigborders</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="bigborders">bigborders </div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders </div>
  <div class="bigborders">bigborders </div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders </div>
  <div class="bigborders">bigborders </div>
  <div>placeholder</div>
  <div>placeholder</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div class="hidden">hidden</div>
  <div>placeholder</div>
  <div class="bigborders">bigborders </div>
  <div class="bigborders">bigborders </div>
  <div>placeholder</div>
</div>

Note:I used a wrapper to wrap the elements because the stack snippet add a hidden div which breaks :last-child selector

Rainbow
  • 6,772
  • 3
  • 11
  • 28
0

Don't worry about the visually adjacent .bigborders. Just take a step back into the HTML and appreciate that you were actually very close to the solution. Use adjacent siblings selector + with the .hidden class instead. See the CSS code below.

.hidden {
    display: none;
}
.bigborders {
    border: 2px solid #AAAAAA;
}
.hidden+.bigborders {
    border-top: 0px;
    border-bottom: 2px solid #AAAAAA;
}

I've applied border on 4 sides for the .bigborders class. But if the preceding element is .hidden the it removes top border to avoid the thickness issue. Of course this won't work if your first element has a hidden class and then next one is .bigborders - but hope this is some start.

JSFiddle link.

Clyde D'Souza
  • 402
  • 2
  • 10
  • Sorry, but I already considered this approach. The problem is that the upper border has to disappear only if all the elements between two .bigborders are hidden, not just the last one. If I remove the hidden class on line 4 or 5 of your code, the upper border shouldn't disappear. – GRB Apr 10 '20 at 23:17
0

I managed to find a solution, although it's a bit bizarre and requires some flexibility. It all revolves around the fact that borders don't really have to be CSS borders. A colored div with a height of 3px between two other divs can be a border as well.

<div>...</div>
<div>...</div>
<div class="bigborder"></div>
<div>...</div>
<div class="bigborder"></div>
<div class="hidden">...</div>
<div class="hidden">...</div>
<div class="hidden">...</div>
<div class="bigborder"></div>
<div>...</div>
<div class="bigborder"></div>
<div>...</div>
<div>...</div>

And the CSS rule is:

.bigborder {
    height: 3px;
    background-color: #AAAAAA;
    margin-top: -3px;
    padding-top: 0;
    padding-bottom: 0;
    border-top-width: 0;
    border-bottom-width: 0;
}

https://jsfiddle.net/4h2j6z9o/

This way I can stack as many .bigborders as I want, and they all overlap. Not exactly the best way to achieve the result, but it doesn't use JS, so I'm satisfied.

I'll wait a couple days to mark this as the solution, in case something better turns up.

GRB
  • 425
  • 1
  • 6
  • 20