En este tutorial aprenderás cómo crear un menú desplegable sencillo con Vanilla JavaScript, HTML y CSS. Vamos a recorrer el código HTML, CSS y JavaScript, pero prestando más atención a la programación, siendo que este es un tutorial de JS. Usaremos un JavaScript y CSS sencillo, sin Frameworks o preprocesadores. La única excepción será importar el archivo CSS de Font Awesome porque usaremos uno de sus iconos.

Este artículo está dirigido a desarrolladores que tienen un entendimiento promedio de HTML, CSS y JavaScript. Traté de hacerlo lo más limpio posible, pero no me enfocaré mucho en los detalles. Espero que lo disfruten.

Capturas

Así es como se ve el resultado del código:

Pantalla inicial:

Menú desplegable abierto:

Menú desplegable con opción seleccionada:

HTML:

En esta sección, abordaremos la implementación del código HTML del demo de la página. Para empezar, veamos el código <head>

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>Ejemplo Desplegable</title>

	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/'-awesome/4.7.0/css/font-awesome.min.css">
	<link rel="stylesheet" href="styles.css">
</head>

Esto es prácticamente HTML la sección repetitiva de "head", con la excepción de las etiquetas link  que cargan los dos archivos CSS a usar en este tutorial: los estilos de Font Awesome y el archivo styles.css, donde definiremos los estilos de esta página.

Luego, tenemos el resto del archivo HTML, el body:

<body>
	<div class='dropdown'>
		<div class='title pointerCursor'>Seleccione una opción <i class="fa fa-angle-right"></i></div>
		
		<div class='menu pointerCursor hide'>
			<div class='option' id='option1'>Opcion 1</div>
			<div class='option' id='option2'>Opcion 2</div>
			<div class='option' id='option3'>Opcion 3</div>
			<div class='option' id='option4'>Opcion 4</div>
		</div>

	</div>
	<span id='result'>El resultado es: </span>
	<script>
	  ...
	</script>
</body>
</html>

Esta sección puede ser dividida en 3 partes principales:

  • El div .dropdown, donde la estructura del elemento desplegable será definida.
  • El elemento #result, que contendrá la opción seleccionada por el usuario, obtenido del elemento desplegable.
  • El script escrito dentro del tag <script>. Su implementación está escondida porque los detalles serán explicados en la última sección de este tutorial.

El menú desplegable es un div que contiene los elementos  title y menu. El primero define el texto que se mostrará en el elemento antes que se elija una opción y el segundo definirá las opciones que podrán ser seleccionadas por el elemento.

El elemento result está allí solo para mostrarte cuál de las opciones está seleccionada actualmente.

Estilos:

A continuación podrás revisar el código CSS completo. Como puedes ver, hace uso de las propiedades CSS3 transition y transform.

Por favor pon atención en las definiciones de clases de .dropdown. Estas son usadas para definir el diseño del componente contenedor del desplegable y también los elementos internos, como el .title (título) y sus .option‘s.

body{
	font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
}

.hide {
    max-height: 0 !important;
}

.dropdown{
	border: 0.1em solid black;
	width: 10em;
	margin-bottom: 1em;
}

.dropdown .title{
	margin: .3em .3em .3em .3em;	
	width: 100%;
}

.dropdown .title .fa-angle-right{
	float: right;
	margin-right: .7em;
	transition: transform .3s;
}

.dropdown .menu{
	transition: max-height .5s ease-out;
	max-height: 20em;
	overflow: hidden;
}

.dropdown .menu .option{
	margin: .3em .3em .3em .3em;
	margin-top: 0.3em;
}

.dropdown .menu .option:hover{
	background: rgba(0,0,0,0.2);
}

.pointerCursor:hover{
	cursor: pointer;
}

.rotate-90{
	transform: rotate(90deg);
}

JavaScript:

Ahora veremos como la parte de JavaScript es implementada. Primero recorreremos las definiciones de funciones y luego el código que llama a dichas funciones para hacer que las acciones desplegables sucedan.

En resumen, son 3 acciones que ocurren dependiendo de cual sea la interacción del usuario, dado que los "listeners" sean agregados a los elementos del DOM.

  1. Dar clic en el elemento desplegable
  2. Seleccionar una de las opciones del desplegable
  3. Cambiar la opción actualmente seleccionada

Me gustaria aclarar que estamos usando funciones flecha (arrow functions) ( () => {} ) y la palabra clave const, que son características de ES6. Probablemente esté todo bien si estás usando una versión actualizada de tu navegador, pero ten esto en cuenta.

1. Dar clic en el elemento desplegable

function toggleClass(elem,className){
	if (elem.className.indexOf(className) !== -1){
		elem.className = elem.className.replace(className,'');
	}
	else{
		elem.className = elem.className.replace(/\s+/g,' ') + 	' ' + className;
	}
	
	return elem;
}

function toggleDisplay(elem){
	const curDisplayStyle = elem.style.display;			
				
	if (curDisplayStyle === 'none' || curDisplayStyle === ''){
		elem.style.display = 'block';
	}
	else{
		elem.style.display = 'none';
	}
}


function toggleMenuDisplay(e){
	const dropdown = e.currentTarget.parentNode;
	const menu = dropdown.querySelector('.menu');
	const icon = dropdown.querySelector('.fa-angle-right');

	toggleClass(menu,'hide');
	toggleClass(icon,'rotate-90');
}

Al dar clic en el menú desplegable, éste se abre (si está cerrado) o se cierra (si está abierto). Esto sucede vinculando toggleMenuDisplay al Listener del evento clic en el elemento desplegable. Esta función alterna la visibilidad de su elemento menu haciendo uso de las funciones toggleDisplay y toggleClass.

2. Seleccionar una de las opciones del desplegable

function handleOptionSelected(e){
	toggleClass(e.target.parentNode, 'hide');			

	const id = e.target.id;
	const newValue = e.target.textContent + ' ';
	const titleElem = document.querySelector('.dropdown .title');
	const icon = document.querySelector('.dropdown .title .fa');


	titleElem.textContent = newValue;
	titleElem.appendChild(icon);
	
	//Activa evento personalizado
	document.querySelector('.dropdown .title').dispatchEvent(new Event('change'));
	//setTimeout se usa para que la transición se muestre correctamente
	setTimeout(() => toggleClass(icon,'rotate-90',0));
}

3. Cambiar la opción actualmente seleccionada

function handleTitleChange(e){
	const result = document.getElementById('result');

	result.innerHTML = 'The result is: ' + e.target.textContent;
}

La función handleTitleChange está sujeta al evento personalizado change en el elemento .title, para cambiar el contenido del elemento #result cada vez que el elemento del título cambie. La activación del evento se da en la sección anterior.

Código principal

//obtiene elementos
const dropdownTitle = document.querySelector('.dropdown .title');
const dropdownOptions = document.querySelectorAll('.dropdown .option');

//vincula listeners a estos elementos
dropdownTitle.addEventListener('click', toggleMenuDisplay);
dropdownOptions.forEach(option => option.addEventListener('click',handleOptionSelected));
document.querySelector('.dropdown .title').addEventListener('change',handleTitleChange);

En la sección principal solo hay código simple para obtener el título de la sección desplegable y para vincular cada opción a los eventos discutidos en la última sección.

Demo

El código completo y el demo de esta aplicación puede ser encontrados aquí.

Traducido del artículo How to Create a Dropdown Menu with CSS and JavaScript