Select an element that comes before another element with the same class in CSS

How to override the margin-bottom: 20px; that was set by the framework using it's predefined class .alert and set it back to 0 when the element is followed by another .alert element.

<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
  
  <div class="alert alert-success" role="alert">The <code>margin-bottom: 20px;</code> was set by the framework.</div>
  <div class="alert alert-info" role="alert">The <code>margin-bottom: 20px;</code> was set by the framework.</div>
  <div class="alert alert-warning" role="alert">The <code>margin-bottom: 20px;</code> was set by the framework.</div>
  
</body>

I know that an adjacent selector can do a trick by selecting only a followed .alert element that is immediately preceded by another .alert element and set it's margin-top to -20px but this sounds dirty.

.alert + .alert {
  margin-top: -20px;
}
<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
  
  <div class="alert alert-success" role="alert">The <code>margin-bottom: 20px;</code> was retracted by the followed <code>margin-top: -20px;</code>.</div>
  <div class="alert alert-info" role="alert">The <code>margin-bottom: 20px;</code> was retracted by the followed <code>margin-top: -20px;</code>.</div>
  <div class="alert alert-warning" role="alert">The <code>margin-bottom: 20px;</code> was not retracted because there's no followed <code>.alert</code> element.</div>
  
</body>

That CSS will retract the margin-bottom: 20px; of a preceded .alert, but is there a way to directly select the .alert if it comes before another .alert to override its margin-bottom to 0?

Upvotes: 2

Views: 5853

Answers (3)

Richie Bendall
Richie Bendall

Reputation: 9172

To select elements that preceed others, use :has(): .alert:has(+ .alert). The selector provided to :has will be used for matching but will not cause the matched element to be selected.

.alert:has(+ .alert) {
  margin-bottom: 0;
}
<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
  
  <div class="alert alert-success" role="alert">The <code>margin-bottom: 20px;</code> was retracted by the followed <code>margin-top: -20px;</code>.</div>
  <div class="alert alert-info" role="alert">The <code>margin-bottom: 20px;</code> was retracted by the followed <code>margin-top: -20px;</code>.</div>
  <div class="alert alert-warning" role="alert">The <code>margin-bottom: 20px;</code> was not retracted because there's no followed <code>.alert</code> element.</div>
  
</body>

Upvotes: 2

Matt Derrick
Matt Derrick

Reputation: 5724

You're asking for a 'previous sibling' selector by the sounds of it which is not possible.

CSS can only select an element if it is a child or adjacently succeeding it. You cannot select an adjacent preceding element (or parent element) in CSS.

Your solution is perfectly acceptable. Negative margins are sometimes seen as 'dirty' for some reason but this is far from true and is documented officially.

Negative values for margin properties are allowed, but there may be implementation-specific limits.

Your solution is the right one!

Upvotes: 4

Oriol
Oriol

Reputation: 288020

If I understand it correctly, you want

.alert {
  margin-bottom: 0;
}
.alert:last-child {
  margin-bottom: 20px;
}

@import 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css';
.alert {
  margin-bottom: 0;
}
.alert:last-child {
  margin-bottom: 20px;
}
<div class="alert alert-success" role="alert">The <code>margin-bottom: 20px</code> was overriden by <code>margin-bottom: 0</code>.</div>
<div class="alert alert-info" role="alert">The <code>margin-bottom: 20px</code> was overriden by <code>margin-bottom: 0</code>.</div>
<div class="alert alert-warning" role="alert">The <code>margin-bottom: 20px</code> was overriden by <code>margin-bottom: 20px</code>.</div>

Upvotes: 3

Related Questions