Tengo una larga lista de elementos de la lista (generados a través de una API) y me gustaría mostrar/ocultar algunos de los elementos con un clic, algo así como un efecto de acordeón.
Los elementos de la lista que me gustaría ocultar de forma predeterminada tienen una clase de elemento P , y la clase de elemento M sobre ellos es lo que debería activarlos para que se muestren.
Aquí está mi marcado:
<li class="session-item M-item"></li> <li class="session-item P-item"></li> <li class="session-item P-item"></li> <li class="session-item G-item"></li>Los artículos P son hijos de su artículo M principal. Entonces, de manera predeterminada en el ejemplo anterior, los dos elementos P deben estar ocultos y el elemento M y el elemento G deben mostrarse. Cuando se hace clic en el elemento M, se muestran los dos elementos P ocultos. Solo estoy enumerando 4 elementos en el ejemplo anterior, pero la lista continúa y el único separador entre esta relación son los elementos G.
Lo hice funcionar pero solo muestra/oculta el primer hermano directo de M-item, usé nextElementSibling , así:
var acc = document.getElementsByClassName("M-item"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function () { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.display === "block") { panel.style.display = "none"; } else { panel.style.display = "block"; } }); }¿Cuál es la mejor manera de seleccionar a todos los hermanos con el elemento P y luego romper, buscar otro elemento M y repetir?
¿Hay algo similar a nextAll(); en vainilla JS? ¿Necesito un bucle while para iterar sobre el siguiente hermano?
Así es como se ve mi marcado completo:
Mi respuesta parece un poco original, pero usa un ciclo while para ocultar todas las siguientes etiquetas <li> siempre que tengan la clase .P-item. El bucle se detiene si el siguiente elemento no tiene esa clase, por lo tanto, solo oculta los elementos P que siguen directamente al elemento M que se presionó. Pruébelo copiando y pegando todo el código siguiente aquí:
<!DOCTYPE html> <head> <meta charset="utf-8"> <style> .M-item { background-color: red; color: blue; } .active { background-color: pink; } .hide { display: none; } </style> </head> <body> <ul class="agenda"> <li class="session-item M-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item M-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item M-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> <li class="session-item P-item">something</li> </ul> <script> var acc = document.getElementsByClassName("M-item"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function () { this.classList.toggle("active"); let next = this.nextElementSibling; while (next.classList.contains('P-item')) { next.classList.toggle('hide'); next = next.nextElementSibling; } }); } </script> </body> </html>Creo que el OP tiene como objetivo encontrar hermanos adyacentes con la misma clase.
Una idea sería intervenir un poco antes, antes de que los li se configuren como hermanos, e imponer una estructura html allí, agrupando a los hermanos de clase similar en div (o ul ). Pero, si comenzamos con la lista ya construida, se puede manejar como una reducción en la colección de li 's...
const sessionItems = document.getElementsByClassName("session-item"); let groups = Array.from(sessionItems).reduce((acc, el, index) => { const pItem = el.classList.contains("p-item") const delimeter = !pItem || index===0; if (delimeter) acc.push([]); if (pItem) acc[acc.length-1].push(el); return acc }, []); // now do whatever we want to the groups, like change visibility // for clarity in this example, add a color class groups.forEach((group, index) => { group.forEach(el => el.classList.add(`color-${index}`)) }); .color-0 { color: red; } .color-1 { color: green; } <ul> <li class="session-item m-item">item A</li> <li class="session-item p-item">item B</li> <li class="session-item p-item">item C</li> <li class="session-item m-item">item D</li> <li class="session-item p-item">item E</li> <li class="session-item p-item">item F</li> <li class="session-item p-item">item G</li> <li class="session-item m-item">item H</li> </ul>Puede mantener la visibilidad de los elementos P solo a través de CSS, según el "activo" que tenga el elemento M anterior. Esto significa que el código JS solo tiene que administrar la alternancia de la clase "activa" en el elemento M:
const ul = document.querySelector(".cad-agenda"); ul.addEventListener("click", function (e) { const mItem = e.target; if (!mItem.classList.contains("M-item")) return; const wasActive = mItem.classList.contains("active"); document.querySelector(".active")?.classList?.remove("active"); mItem.classList.toggle("active", !wasActive); }); .P-item { display: none } .active ~ .P-item:not(.active ~ .M-item ~ .P-item) { display: block } .M-item { cursor: pointer; font-weight: bold } <ul class="cad-agenda accordion accordion-1"> <li class="session-item M-item">M-1</li> <li class="session-item P-item">P-1.1</li> <li class="session-item P-item">P-1.2</li> <li class="session-item G-item">G-1</li> <li class="session-item M-item">M-2</li> <li class="session-item P-item">P-2.1</li> <li class="session-item P-item">P-2.2</li> <li class="session-item M-item">M-3</li> <li class="session-item P-item">P-3.1</li> <li class="session-item P-item">P-3.2</li> <li class="session-item Press-item">PressItem</li> <li class="session-item M-item">M-3</li> <li class="session-item P-item">P-3.1</li> <li class="session-item M-item">M-4</li> <li class="session-item P-item">P-4.1</li> <li class="session-item M-item">M-5</li> <li class="session-item P-item">P-5.1</li> <li class="session-item G-item">G-2</li> <li class="session-item M-item">M-6 (has no P)</li> <li class="session-item -item">empty 1</li> <li class="session-item -item">empty 2</li> <li class="session-item -item">empty 3</li> <li class="session-item M-item">M-7 (has no P)</li> <li class="session-item M-item">M-8</li> <li class="session-item P-item">P-8.1</li> <li class="session-item P-item">P-8.2</li> </ul>