Evitar múltiples toggle con misma clase – javascript

Pregunta:


Para un evento toggle con puro javascript lo desarrollo de la siguiente forma:

Javascript

 // Esta clase lo expande.
const expandButton = document.querySelector('.expand-button');
expandButton.addEventListener('click', ()=> {
    //Esta es la clase que muestra el contenido.
    const more = document.querySelector('.more');
   //El contenido pasa de oculto a ser visible.
   more.style.display = 'block';
  //Oculto la imagen al expandir.
   if (more.style.display === 'block') {
       expandButton.style.display = 'none';
   }
}, false);

HTML

<div class="more"> Contenido 1 </div> <!-- El contenido -->
<div class="expand-button">Expandir</div> <!-- Botón que expande -->

El problema está en que si tengo otro toogle con la misma clase

<div class="more"> Contenido 2 </div> <!-- El contenido -->
<div class="expand-button">Expandir</div> <!-- Botón que expande -->

Debería de recuperar los elementos con la clase expand-button con un for(), cuando lo hago, el botón expandir del segundo toggle abre al primero, y el primero realiza su función correctamente.

¿Alguna solución?

Preguntado por: UnderZero_-

Alvaro Montoro

El problema ocurre por esta línea de código:

const more = document.querySelector('.more');

Como haces la selección con querySelector y hay varios elementos con la clase “more”, entonces el navegador se queda sólo con el primero, pulses en el botón de expandir que pulses.

Una posible solución sería, si puedes, añadir un data-atributo al botón de expandir que le indique cuál de los .more debe abrirse (y añadirle un id a cada uno de los contenedores). Algo como esto:

<div id="contenido1" class="more"> Contenido 1 </div>
<div class="expand-button" data-toggle="#contenido1">Expandir</div>

<div id="contenido2" class="more"> Contenido 2 </div>
<div class="expand-button" data-toggle="#contenido2">Expandir</div>

Entonces el JavaScript cambiaría para seleccionar no el “.more” sino el id especificado en el data-toggle. Aquí dejo un ejemplo funcionando:

const expandButton = document.querySelectorAll('.expand-button');

expandButton.forEach(function(el, arr) {
    el.addEventListener('click', (e)=> {
        //Esta es la clase que muestra el contenido.
        var more = document.querySelector(e.target.dataset.toggle);
        //El contenido pasa de oculto a ser visible.
        more.style.display = 'block';
        //Oculto la imagen al expandir.
        if (more.style.display === 'block') {
            el.style.display = 'none';
        }
    }, false);
});
.more { display:none }
<div id="contenido1" class="more"> Contenido 1 </div>
<div class="expand-button" data-toggle="#contenido1">Expandir</div>

<div id="contenido2" class="more"> Contenido 2 </div>
<div class="expand-button" data-toggle="#contenido2">Expandir</div>

Actualización: como se menciona en los comentarios, el código presenta problemas en algunas versions de Internet Explorer. Eso ocurre porque se usan un par de cosas (forEach y la notación => para funciones) que no están bien soportadas en versiones “antiguas” de IE.

Estos problemas se pueden solucionar de manera simple:

  • Cambiando el forEach por un bucle for simple (y las referencias a el por referencias al elemento dentro del array que se atraviesa)
  • Cambiando (e) => { ... } por el tradicional function(e) { ... }

Con esos dos cambios, el código ya funciona en Chrome, Firefox e Internet Explorer:

const expandButton = document.querySelectorAll('.expand-button');

for (var x = 0; x < expandButton.length; x++) {
    expandButton[x].addEventListener('click', function(e) {
        //Esta es la clase que muestra el contenido.
        var more = document.querySelector(e.target.dataset.toggle);
        //El contenido pasa de oculto a ser visible.
        more.style.display = 'block';
        //Oculto la imagen al expandir.
        if (more.style.display === 'block') {
            e.target.style.display = 'none';
        }
    }, false);
}
.more { display:none }
<div id="contenido1" class="more"> Contenido 1 </div>
<div class="expand-button" data-toggle="#contenido1">Expandir</div>

<div id="contenido2" class="more"> Contenido 2 </div>
<div class="expand-button" data-toggle="#contenido2">Expandir</div>

Puedes navegar el DOM desde el botón hacia el hermano anterior, usando la propiedad previousSibling.

De esta manera obtienes el <div> anterior en la estructura y expandes unicamente ese.

Algo así: (fíjate que uso this.previousSibling lo cual selecciona únicamente el hermano anterior del botón sobre el que haces click)

const expandButton = document.querySelectorAll('.expand-button');
expandButton.addEventListener('click', ()=> {

   const more = this.previousSibling; // aqui se usaria

   //El contenido pasa de oculto a ser visible.
   more.style.display = 'block';
  //Oculto la imagen al expandir.
   if (more.style.display === 'block') {
       expandButton.style.display = 'none';
   }
}, false);

Ten en cuenta que esto funciona con la estructura HTML que pones en la pregunta, si cambias la estructura, sera necesario modifacar el codigo para navegar al elemento correcto.

Te recomiendo que leas sobre otras propiedades similares del objeto Node tales como nextSibling, parentNode, etc.

MAS..

Me acabo de dar cuenta que la forma en que estableces el listener no funcionara para todos los botones con la clase .expand-button. Esto se debe a que querySelector toma únicamente el primer elemento que cumpla con la query.

Para asignar el handler a todos los botones deberas usar querySelectorAll y luego iterar sobre el resultado para asignar el manejador a todos los elementos.

Para ES2015:

var expandButtons = document.querySelectorAll('.expand-button');

Array.from(expandButtons).forEach(button => {
    button.addEventListener('click', function(event) {

        const more = this.previousSibling; 
        /// resto del código aquí.

    });
});

La Respuesta que ofreció Alvaro Montoro es correcta, pero tiene un handicap, IE no soporta forEach(), así que siguiendo la linea que nos muestra Alvaro tuve que cambiar el forEach por un for, de esta forma el toggle funciona también en IE.

Espero que os sirva de ayuda como me lo ha sido a mi.

const expandButton = document.querySelectorAll('.expand-button');
for (let i = 0; i < expandButton.length; i++) {
    var element = expandButton[i];

    element.addEventListener('click', (e) => {
        var more = document.querySelector(e.target.dataset.toggle);
        more.style.display = 'block';   
    }, false);
}
.more { display:none }
<div id="contenido1" class="more"> Contenido 1 </div>
<div class="expand-button" data-toggle="#contenido1">Expandir</div>

<div id="contenido2" class="more"> Contenido 2 </div>
<div class="expand-button" data-toggle="#contenido2">Expandir</div>

Fuente

Related Posts:

¿Cómo obtener la respuesta de una llamada asíncrona (AJAX) fuera de ella? – javascript jquery asincrónico
Pregunta: Actualmente tengo esta función: function devuelveButaca(posicion){ var array = posicion.split('_'); var row = array; var column ...
Validación de datos nulos en una función – javascript
Pregunta: Tengo la siguiente función pero al momento validar todos mis datos me los registra todos a excepción del los "containers" que son grids y ...
Como poner una imagen de fondo a pantalla completa – javascript jquery html5
Pregunta: Como puedo hacer que la imagen abarque todo el tamaño de la pantalla. Se que de las propiedad min-width y min-heigth pero ellas no ...
Error de: Uncaught TypeError: url.indexOf is not a function por Jquery – javascript jquery
Pregunta: En el navegador, reviso en inspeccionar elemento, y me sale el siguiente error: Uncaught TypeError: url.indexOf is not a function Me imagino que es ...
Como validar array checkbox – javascript php jquery
Pregunta: Que tal buen dia, alguien me podria decir como se puede hacer para que cuando presione el boton "BAJAR DATOS" si este esta vacio ...
Uso de js y c# en unity. ¿Igual rendimiento? – javascript c# unity3d
Pregunta: Estoy comenzando con unity . Y veo que se pueden usar c# y js para programar. Mi pregunta es si tanto c# como js ...
Como cambiar una propiedad html dependiendo del valor de un perfil recogido de clase java – javascript jquery html
Pregunta: Tengo una caja de texto en la que me gustaría poder escribir o no dependiendo del perfil que utilice la aplicación. Esta caja de ...
Agregar marcador especifico a una localización Google Maps – javascript google-maps
Pregunta: ¿Cómo podría añadir un marcador personalizado en Google maps? Actualmente el código que tengo es el siguiente: var locations = [ ['Bondi Beach', -33.890542, ...
Usar variable en otra función con javascript – javascript html app
Pregunta: Primero que todo voy a dejar un enlace donde pueden ver el ejemplo funcionando de manera completa. Enlace a la pagina AQUI El valor elegido en ...
Ocultar y Mostrar con Mapa de Google – javascript c# asp.net
Pregunta: Tengo un div en mi página ASP.NET que contiene un Mapa de Google, y necesito mostrarlo y ocultarlo pero no me funciona. Este es el ...
Copiar valor a un input que esta dentro de un footer de un datatable – javascript jquery datatables
Pregunta: Estoy usando los DataTables de jQuery y me estoy encontrando con el siguiente problema: cuando pulso en un botón necesito pasarle un valor con ...
¿Cómo rellenar cada circulo al pasar el mouse encima? – javascript jquery html
Pregunta: ¡Hola! Estoy practicando jQuery, y me gustaría poder hacer como un ranking. A lo que quiero llegar es que cada vez que el mouse ...
convertir array a Map en javascript – javascript map nodejs
Pregunta: quiero convertir un arreglo a Map(), en este console "console.log(arrayParaMap);" sale undefined el ejemplo que uso es este //example {a:1,a:2,a:1,b:1,b:5,b:6} el map que prentendo obtener es ...
como iterar un objeto en nodejs – javascript nodejs
Pregunta: Hice esta función: const obj = ; const objMapped = obj.reduce((acc, item) => { ...
¿Cómo mostrar cualquier contenido HTML con ng-bind-html y $sce? – javascript angularjs json
Pregunta: El contenido HTML de mi página está compuesto por archivos JSON, todas las etiquetas se muestran correctamente, como las <h1>, <p>, <img>, etc... Pero ...

Add a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *