Reputation: 46796
How can I select the first of the highest h*
elements present in a DOM?
Something like
(h1, h2, h3, h4, h5, h6):first-of-ordered-set
I.e. if a DOM tree has, in this order, h2
, h3
, h1
, h2
, h1
, it would select the first h1
;
and if the DOM has h3
, h3
, h2
, h2
, h4
, it would select the first h2
.
Let's assume h*
elements are not nested.
I suspect CSS doesn't have that power, right?
Somethink potentially usable: https://css-tricks.com/extremely-handy-nth-child-recipes-sass-mixins/
Edit: Why I want it: A CMS system takes this "first top heading" as a title of the document (post, page, ...). But it leaves it in the page. And then it shows the title twice - once as the post title and once in the body. JavaScript is removed. The top h*
level may differ.
Upvotes: 5
Views: 1350
Reputation: 272744
The main issue is that we don't have previous selector in CSS. For example, we can get the first h2
but if later we find a h1
we cannot have a selector to go backwards.
Here is the best you can do with CSS. You can hide all the elements after the needed one (so the element you want is the last visible one) but you cannot do the same with the previous elements.
h1:first-of-type,
h2:first-of-type,
h3:first-of-type,
h4:first-of-type {
color:red;
}
h1:first-of-type ~ * {
display:none;
}
h2:first-of-type ~ *:not(h1) {
display:none;
}
h3:first-of-type ~ h4 {
display:none;
}
.container {
border:1px solid;
}
<div class="container">
<h3>text 3</h3>
<h1>text 1*</h1>
<h2>text 2</h2>
<h1>text 1</h1>
</div>
<div class="container">
<h3>text 3</h3>
<h2>text 2*</h2>
<h2>text 2</h2>
<h4>text 1</h4>
</div>
<div class="container">
<h3>text 3</h3>
<h3>text 2</h3>
<h2>text 2*</h2>
<h4>text 1</h4>
</div>
<div class="container">
<h1>text 3*</h1>
<h3>text 2</h3>
<h2>text 2</h2>
<h4>text 1</h4>
</div>
You may then combine with a small JS code to keep only the needed element:
$('h3:first-of-type').prevAll('h4').hide();
$('h2:first-of-type').prevAll('*:not(h1)').hide();
$('h1:first-of-type').prevAll('*').hide();
h1:first-of-type,
h2:first-of-type,
h3:first-of-type,
h4:first-of-type {
color:red;
}
h1:first-of-type ~ * {
display:none;
}
h2:first-of-type ~ *:not(h1) {
display:none;
}
h3:first-of-type ~ h4 {
display:none;
}
.container {
border:1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<h3>text 3</h3>
<h1>text 1*</h1>
<h2>text 2</h2>
<h1>text 1</h1>
</div>
<div class="container">
<h3>text 3</h3>
<h2>text 2*</h2>
<h2>text 2</h2>
<h4>text 1</h4>
</div>
<div class="container">
<h3>text 3</h3>
<h3>text 2</h3>
<h2>text 2*</h2>
<h4>text 1</h4>
</div>
<div class="container">
<h1>text 1*</h1>
<h3>text 2</h3>
<h2>text 2</h2>
<h4>text 1</h4>
</div>
I used to make the element hidden but you can do the same with other styles:
$('h3:first-of-type').prevAll('h4').addClass('initial');
$('h2:first-of-type').prevAll('*:not(h1)').addClass('initial');
$('h1:first-of-type').prevAll('*').addClass('initial');
h1:first-of-type,
h2:first-of-type,
h3:first-of-type,
h4:first-of-type {
color:red;
font-family:cursive;
font-style:italic;
}
h1:first-of-type ~ *,
h2:first-of-type ~ *:not(h1),
h3:first-of-type ~ h4,
h1.initial,h2.initial,h3.initial,h4.initial{
color:initial;
font-family:initial;
font-style:initial;
}
.container {
border:1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<h3>text 3</h3>
<h1>text 1*</h1>
<h2>text 2</h2>
<h1>text 1</h1>
</div>
<div class="container">
<h3>text 3</h3>
<h2>text 2*</h2>
<h2>text 2</h2>
<h4>text 1</h4>
</div>
<div class="container">
<h3>text 3</h3>
<h3>text 2</h3>
<h2>text 2*</h2>
<h4>text 1</h4>
</div>
<div class="container">
<h1>text 1*</h1>
<h3>text 2</h3>
<h2>text 2</h2>
<h4>text 1</h4>
</div>
Upvotes: 1
Reputation: 46796
I found something. CSS can't do it. Yet.
W3C is drafting new features:
.post-content:has(h1, h2, h3, h4, h5, h6)
Together with :not()
, this will allow what I need:
.post-content:has(h1) h1:first-of-kind,
.post-content:not(:has(h1)) h2:first-of-kind,
.post-content:not(:has(h1,h2)) h3:first-of-kind,
...
There's a catch - the :not()
currently can only have a single "simple selector". If it supported more, then it would be achievable even without :has
.
Greetings to the future readers, I hope it worked out. For now, I am leaving this open, maybe someone will figure out with CSS 3.1.
Upvotes: 1