Style elements between two headings

I would like to use CSS to select elements that come after h5 headings. However, I do not want to target the elements if their more recent sibling is an h4. Is there a way to use general sibling selectors and specificity to achieve this? Here is an example: https://codepen.io/Jimmy-Revelino/pen/PoxmNQr JS would also be an option, if that opened up possibilities.
13 Replies
Myndi
Myndi12mo ago
The best is to: 1. Share a CodePen 2. Share the repository 3. Share the live version 4. Share descriptive images In that order of importance, if one doesn't apply, go for the other. If it's just "a thought" or "idea", sharing a sample would also work (or even drawing it as last resort...).
JRev3
JRev312mo ago
Thank you. I've updated my original post.
croganm
croganm12mo ago
Maybe this? h5:not(~h4) ~ .target Replace target with what you want Mmmm Still not sure that works actually It's difficult to know exactly what you're looking for without a codepen
JRev3
JRev312mo ago
Sorry, forgot to link my codepen. Updaed the original post again.
croganm
croganm12mo ago
const containerDiv = document.querySelector("div");
const children = Array.from(containerDiv.children);

let startSelection = false;
const selectedElements = [];

for (const child of children) {
if (child.tagName === 'H5') {
startSelection = true;
} else if (child.tagName === 'H4') {
startSelection = false;
} else if (startSelection) {
console.log(child)
child.classList.add("selected")
}
}
const containerDiv = document.querySelector("div");
const children = Array.from(containerDiv.children);

let startSelection = false;
const selectedElements = [];

for (const child of children) {
if (child.tagName === 'H5') {
startSelection = true;
} else if (child.tagName === 'H4') {
startSelection = false;
} else if (startSelection) {
console.log(child)
child.classList.add("selected")
}
}
It's not very performant, but wrap all of those tags within something (such as a div), and then select the div in the containerDiv variable. You can select the div just like you do in CSS This adds the "selected" class to the tags you want to style (you can change the class name at the bottom) Then just create a css rule with that class name doing whatever you want
JRev3
JRev312mo ago
That's an interesting idea. I will try that. I would like to make collapsible sections in this way. (I know not ideal, but the help file is already made, so less work to be able to implement it just from CSS/JS). But I think this might work. I can use the ids of the h5 to add unique classes to each selection with this approach. Thanks! I'll update after I test it.
Kevin Powell
Kevin Powell12mo ago
There's a way to do this with :has() that's escaping me at the moment, but of course, it depends if you're comfortable using :has() or not, since browser support isn't the best at the moment
JRev3
JRev312mo ago
Kevin, thanks for weighing in! I am fine using :has, as it would be a plus if there's browser support but not a big deal if not
Kevin Powell
Kevin Powell12mo ago
I've been playing around trying to get it to work, and Google is failing me here, lol. I'm trying to remember how it works... if I do, I'll post it here
croganm
croganm12mo ago
I'd love to see how to do it in CSS myself! Was playing around with it and couldn't quite get it. A little tough to do on mobile lol. That's why I just whipped up a quick JS solution, but I feel like this is definitely possible using CSS and has. Also, apologies Kevin but I accidently reported your message when trying to reply 😬. Context menu opened in a different position and muscle memory just took over. Just wanted to give a heads up!
Kevin Powell
Kevin Powell12mo ago
no problem, I'm sure it'll be fine 😄 I might have been thinking about this, which doesn't work how you need it to:
h5 + p:has(~h4) { ... }
h5 + p:has(~h4) { ... }
That would select everything from and h5, until it reaches an h4, but only if you did it once... it won't keep going after the h4, and if you have multiple h4, it breaks things too
JRev3
JRev312mo ago
Really appreciate the help! I think the javascript approach actually helped me consider some new ideas. I updated the codepen with this code. I'm able to create collapsable sections without adding new divs.
/ Get h5s
const h5Elements = document.querySelectorAll('H5');

// Counter for dynamic id
let idCounter = 0;

// add dynamic ids if element does not have one
h5Elements.forEach( element => {
console.log( element.id)
if ( element.id === '' ) {
element.id = `id${idCounter}`;
idCounter++;
}

// add click listener
element.addEventListener('click', function( event ) {
let sectionID = this.id

const elementsInSection = document.querySelectorAll(`.selected-${sectionID}`)

elementsInSection.forEach( el => {
if( el.classList.contains('display') ) {
el.classList.remove('display');
} else {
el.classList.add('display');
}
}) //end for elementsInSection each

}) //End event listener


}) //end h5Elements for each

// get all elements in document to add selector class
const elements = document.querySelectorAll("div *");

let startSelection = false;
let id = ''; // for adding dynamic classes

// Add selected classes
elements.forEach( element => {
if ( element.tagName === 'H5') {
startSelection = true;
id = element.id
} else if ( element.tagName === 'H4' ) {
startSelection = false;
} else if ( startSelection ) {
element.classList.add(`selected-${id}`);
}
})
/ Get h5s
const h5Elements = document.querySelectorAll('H5');

// Counter for dynamic id
let idCounter = 0;

// add dynamic ids if element does not have one
h5Elements.forEach( element => {
console.log( element.id)
if ( element.id === '' ) {
element.id = `id${idCounter}`;
idCounter++;
}

// add click listener
element.addEventListener('click', function( event ) {
let sectionID = this.id

const elementsInSection = document.querySelectorAll(`.selected-${sectionID}`)

elementsInSection.forEach( el => {
if( el.classList.contains('display') ) {
el.classList.remove('display');
} else {
el.classList.add('display');
}
}) //end for elementsInSection each

}) //End event listener


}) //end h5Elements for each

// get all elements in document to add selector class
const elements = document.querySelectorAll("div *");

let startSelection = false;
let id = ''; // for adding dynamic classes

// Add selected classes
elements.forEach( element => {
if ( element.tagName === 'H5') {
startSelection = true;
id = element.id
} else if ( element.tagName === 'H4' ) {
startSelection = false;
} else if ( startSelection ) {
element.classList.add(`selected-${id}`);
}
})
croganm
croganm12mo ago
That's always the best, when you take it an make it your own! Good job