Como hacer tu Acordeón personalizado con GenerateBlocks
En este truco os explico paso a paso cómo hacer tu Acordeón personalizado con GenerateBlocks para que puedas utilizarlo en tus paginas webs
Planteamiento del problema
Se me ha plantado un requerimiento de un cliente que implicaba tener el típico acordeón que permite disponer contenido en distintos paneles que se van abriendo y cerrando según vayamos pinchando en las cabeceras de dicho acordeón. Para que os hagáis una idea de la estética y funcionalidad que estaba buscando os dejo aquí una muestra del diseño que hay que implementar.
En la foto vemos tres paneles con imagen que tienen un titular y una flecha, la idea es que cuando el usuario pinche en cualquiera de estas tres cabeceras se abra un contenedor debajo de esta cabecera y muestre el contenido de ese panel, al volver a pinchar sobre esa cabecera este panel se recoge y se cierra.
Hice una búsqueda bastante extensa de los plugin que podía haber para este tipo de componente Acordeón, pero no tuve mucha suerte, así que me puse a investigar como podría hacerlo usando componentes que me resultasen cómodos a la hora de maquetar, que fueran responsive y que además estuvieran dentro del plugin GenerateBlocks que suelo usar con GeneratePress.
Recursos a utilizar
Descubrí que GenerateBlocks tiene una funcionalidad que se llama las plantillas locales “Local Templates” y que podemos crear un Bloque de Gutemberg a base de un conjunto de bloques que después usemos en cualquier sitio de nuestra web.
Añado un nuevo Local Template y configuro el nombre de la plantilla y una imagen destacada para que, me resulte fácil reconocer este elemento en el menú de bloques cuando vaya a insertarlo en la pagina donde lo quiero utilizar.
Ahora voy a montar la estructura del componente, añadiendo un contenedor GenerateBlock al que le voy a añadir la clase .accordion
en las propiedades avanzadas del contenedor, para que toda la estructura que cree a continuación quede envuelta en un div
con la clase .accordion
.
Después, para cada una de las cabeceras que quiero en mi acordeón, creo un contenedor que a su vez, contiene una cuadricula con 3 columnas, la primera de la izquierda es solo para añadir espacio y poder jugar con la posición de las otras dos columnas, al tener 3 me resulta mas sencillo jugar con el porcentaje de ancho de cada columna y centrar el titulo de la cabecera y el icono que indica si el panel esta abierto o cerrado. Para esta parte del icono, he utilizado un bloque de Titular, también de generateBlocks al que le he puesto un Icono y he activado la opción de ocultar el texto.
Para el contenido de los paneles he dispuesto un contenedor debajo de cada cabecera y dentro de ese contenedor, que va a ser el panel que se habrá y se cierre cuando hagamos clic en la cabecera, podemos maquetar el contenido que queramos meter.
En los tres contenedores de cabecera hemos puesto las imágenes como fondo del contenedor, esto te va a permitir un control extra sobre la transparencia de la imagen para poder solucionar cualquier problema de lectura que te presente el texto de las cabeceras.
Hay que poner la clase .click
a los tres contenedores de la cabecera, esto va a permitir después que el CSS hasta su trabajo.
Os explico cual es la lógica detrás de este montaje. tenemos que cambiar el curso a un cursos de puntero cuando el usuario pase el ratón por encima de la cabecera en la que puede hacer clic. Cuando el usuario haga clic, tenemos que expandir y mostrar el contenedor que hay justo debajo del contenedor de cabecera, que es, por decirlo de alguna manera el contenido de esa cabecera.
Introduciendo el codigo necesario
Esto lo vamos a hacer con un poco de CSS y otro poco de Javascript, que vamos a montar muy fácilmente en la pagina web con el uso de un plugin que se llama Code Snippets o Fragmentos de Código. este plugin es muy socorrido cuando quieres poner código en tu tema sin editar el archivo functions.php
o sin tener que subir archivos por FTP a tu web.
Una vez que tenemos instalado el plugin de fragmentos de código, solo tenemos que añadir un nuevo fragmento, asignarle un nombre a nuestra fragmento de código y pegar este código en la caja que nos aparece a continuación. Después guardamos el fragmento y lo activamos, no te olvides de activar el fragmento o de lo contrario no se va a ejecutar.
add_action( 'wp_head', function () { ?> <style> .site .click, .editor-styles-wrapper .click { cursor: pointer; user-select: none; } .click + * { opacity: 0; height: 0; overflow: hidden; margin-bottom: 0 !important; } .click + .open{ opacity: 1; } .rotated { transform: rotate(180deg); } </style> <?php } );
Si observamos el código vemos que es realmente css, pero esta metido en una funciona de WordPress, add_action( ‘wp_head’, function () { } ); que lo que hace es que carga este fragmento de código entre las etiquetas html <head></head> de la pagina, después lo único que hay es código css muy sencillo, que especifica que aquellos elementos que tienen la clase .click
tienen una opacidad de 0, por decirlo de alguna manera están ocultos no están visibles, Pero en cuanto uno de estos elementos recibe la clase .open
, eso quiere decir que su opacidad tiene que pasar a valer 1 y de esta forma se verá el contenedor que hay debajo de la cabecera en la que el usuario acaba de hacer clic.
Otra instrucción css es que aquellos elementos que tengan la clase .rotated
. tienen que estas rotados 180 grados, esta es la flecha que hay al lado del titulo de cabecera que cuando muestre el contenido se girara para indicar que al volver a pinchar en la cabecera, esta se cerrará.
Ahora vamos a añadir el código que implementa la funcionalidad, para ello vamos a añadir otro fragmento de código nuevo, le damos un nombre y pegamos este código en la caja de texto.
add_action( 'wp_footer', function () { ?> <script> window.addEventListener("DOMContentLoaded", () => { const banners = Array.from(document.querySelectorAll(".click")); banners.forEach( element => element.nextElementSibling.setAttribute( 'data-collapsed', 'true' ) ); function toggle() { const handleClick = (event) => { event.currentTarget.classList.toggle("toggled"); // mark it as toggled event.currentTarget.querySelector('.gb-icon').classList.toggle('rotated'); let currentPanel = event.currentTarget.nextElementSibling; // next content currentPanel.classList.toggle("open"); // mark it as open currentPanel.style.transition = 'ease all 0.25s'; // set the css transition let panelHeight = currentPanel.scrollHeight; // get the DOM height let openPanel = currentPanel.classList.contains('open'); // for conditions bellow // if the pannel is open make it heigh enough if not make it zero heigh if ( openPanel ) { currentPanel.style.height = panelHeight + 'px'; currentPanel.setAttribute( 'data-collapsed', 'false' ); window.scroll({top: window.scrollY + 200,left:0, behavior:'smooth'}); } else { currentPanel.style.height = ''; currentPanel.setAttribute( 'data-collapsed', 'true' ); window.scroll({top: window.scrollY - 200,left:0, behavior:'smooth'}); } }; // run the clicking const clicker = banners.forEach((element) => { element.addEventListener("click", handleClick, false); // mouse }); return clicker; } toggle(banners ); }); </script> <?php } );
Al igual que el fragmento anterior, el código esta envuelto en una función de WordPress, que en este caso, hace que este código se cargue en el pie de pagina dentro de la etiqueta <footer> ( wp_footer ) que es donde debe cargarse el codigo Javascript de la misma. Lo primero que hacemos es esperar a que se haya cargado la pagina para estar seguros de que tenemos todos los elementos del DOM.
window.addEventListener("DOMContentLoaded"
Después vamos buscar los elementos que tiene la clsa .click y a establecer atributos
const banners = Array.from(document.querySelectorAll(".click"));
banners.forEach( element => element.nextElementSibling.setAttribute( 'data-collapsed', 'true' ) );
Luego hacemos un funcion que se llama toggle() y que engloba toda la logica de abrir y cerrar los paneles
function toggle() { const handleClick = (event) => { event.currentTarget.classList.toggle("toggled"); // mark it as toggled event.currentTarget.querySelector('.gb-icon').classList.toggle('rotated'); let currentPanel = event.currentTarget.nextElementSibling; // next content currentPanel.classList.toggle("open"); // mark it as open currentPanel.style.transition = 'ease all 0.25s'; // set the css transition let panelHeight = currentPanel.scrollHeight; // get the DOM height let openPanel = currentPanel.classList.contains('open'); // for conditions bellow // if the pannel is open make it heigh enough if not make it zero heigh if ( openPanel ) { currentPanel.style.height = panelHeight + 'px'; currentPanel.setAttribute( 'data-collapsed', 'false' ); window.scroll({top: window.scrollY + 200,left:0, behavior:'smooth'}); } else { currentPanel.style.height = ''; currentPanel.setAttribute( 'data-collapsed', 'true' ); window.scroll({top: window.scrollY - 200,left:0, behavior:'smooth'}); } }; // run the clicking const clicker = banners.forEach((element) => { element.addEventListener("click", handleClick, false); // mouse }); return clicker; }
En esta primera linea de la funcion añadimos la clase .toggled
a las cabeceras donde se hace clic el ratón
event.currentTarget.classList.toggle("toggled");
La siguiente línea añade en forma de conmutador, si la tiene la quita y si no la tiene la pone, la clase .rotated
al icono que hay a la derecha del titular de la cabecera
event.currentTarget.querySelector('.gb-icon').classList.toggle('rotated');
En las siguientes líneas asignamos el contenedor que hay debajo del elemento sobre el que se ha hecho clic nextElementSibling
a la variable CurrentPanel
. y añadimos en modo conmutación la clase .open
a este elemento y realizamos la transición para que se muestre el contenedor con una animación de un cuarto de segundo.
let currentPanel = event.currentTarget.nextElementSibling; // next content
currentPanel.classList.toggle("open"); // mark it as open
currentPanel.style.transition = 'ease all 0.25s'; // set the css transition
En las siguientes dos líneas asignamos la altura actual del navegador (según resolución de pantalla ) a la variable panelHeight
. y asignamos a la variable openPanel
el objeto que tiene la clase .open
let panelHeight = currentPanel.scrollHeight; // get the DOM height
let openPanel = currentPanel.classList.contains('open'); // for conditions bellow
Ahora un bloque condicional que evalúa la condición de si el contenedor que tiene el contenido, añade pixeles de altura al panel y fija el atributo 'data-collapse
‘ en ‘false
‘ ya que esta abierto y por tanto no esta colapsado. Tamiben hace un pequeño scroll hacia abajo de 200 pixeles con respecto a la posición actual de la pagina para mostrar parte del contenido de ese panel. Si la condición es false
, y el contenedor no esta abierto, la altura del contenedor se fija a 0, el atributo ‘data-collapse
‘ se fija en ‘true
‘ y se hace un scroll hacia atras de 200 pixels con respecto a la posición vertical de la pagina.
if ( openPanel ) { currentPanel.style.height = panelHeight + 'px'; currentPanel.setAttribute( 'data-collapsed', 'false' ); window.scroll({top: window.scrollY + 200,left:0, behavior:'smooth'}); } else { currentPanel.style.height = ''; currentPanel.setAttribute( 'data-collapsed', 'true' ); window.scroll({top: window.scrollY - 200,left:0, behavior:'smooth'}); }
En estas líneas se evalúa el evento de ratón y devuelve el elemento que ha recibido el clic.
const clicker = banners.forEach((element) => {
element.addEventListener("click", handleClick, false); // mouse
Finalmente con esta línea se llama a la función para que se ejecute y se le pasa la variable que contiene los elementos de cabecera del acordeón
toggle(banners );
Si vemos esto funcionando en la pagina web, veremos que cuando pulsamos en una de las cabeceras, su contenido se despliega hacia abajo y el icono que hay a la derecha de la cabecera se ha rotado 180º, para volverse a rota y encoger el contenido si volvemos a pinchar en la cabecera.
Espero que este truco te haya resultado útil si tienes como yo, la necesidad de hacer un acordeón tan visual como este. Este truco realmente funcionaria en cualquier tema o framework de tema, la funcionalidad esta basada en clases css, asi que siempre que tengas las clases que aparecen en el tutorial, asignadas a los elementos correspondientes, tendría que funcionarte.
Te dejo este otro par de trucos que tambien te pueden venir muy bien:
- Mostrar Posts relacionados sin usar plugins en WordPress 5
- WordPress 5.9 – Cambiar color del menú según la categoría del post
¡Que tenga un Ingenioso día!
¡que tengas un ingenioso día!
Contrata tu plan Ingenios@ de Sistemas por 5€ al mes y responderé a todas tus preguntas sobre tecnología en el menor tiempo posible. Pasa a formar parte de la comunidad Ingenios@s de Sistemas y disfruta de contenido exclusivo y aprende sobre sistemas Open Source, Blockchain, SmarContract, Web3, Inteligencia Artificial y Reaidad Virtual, súbete al tren de la Revolución 4.0
Si quieres estar al día y no perderte nada Suscribete al Podcast Ingenios@s de Sistemas, un episodio diario que te mantendrá informado y formado en esta vertiginosa carrera.