Las pestañas son un patrón de diseño simple en el que una fila de enlaces es, obviamente, una navegación en la que se puede hacer clic y cuando se hace clic en un enlace, se muestra contenido nuevo. Hay muchas variaciones, por supuesto, pero es uno de los patrones de diseño de navegación más ubicuos que existen. Cuando se organiza en una fila horizontal, también es uno de los patrones de diseño menos amigables con las pantallas pequeñas que existen.
Sin embargo, podemos hacer que funcione.
Sin hacer nada en absoluto para ayudar a las pestañas en una pantalla pequeña, se encontrará con algún tipo de problema
- Dejas que la página se “aleje” y las pestañas son pequeños botones táctiles.
- Dejas que la página tenga el ancho del dispositivo y …
- no hay espacio para las pestañas, por lo que se cortan.
- las pestañas se envuelven, se ven raras y ocupan demasiado espacio.
Básicamente: tenemos que hacer algo aquí para mejorar las pestañas para pantallas pequeñas.
Esto se ha hecho antes
Las soluciones populares incluyen:
- Navegación de palanca de Brad Frost
- Opción de navegación “Fuera del lienzo” como esta.
- Convierte las pestañas en un menú desplegable.
Me gustaría volver a hacerlo con unos objetivos concretos.
Objetivos
Este es el plan:
- Las “pestañas” normales se ven cuando hay espacio, el menú desplegable cuando no lo hay.
- La pestaña “Actual” siempre se muestra y es obvia.
- Funciona con pestañas #hash donde todo el contenido está en la página y los paneles de contenido están ocultos / mostrados.
- Funciona con pestañas vinculadas donde las pestañas se vinculan a una URL diferente.
- El HTML es semántico.
- Hay una versión del HTML que no cambia.
- Hay una versión de JavaScript que no cambia.
- Puede vincular a una pestaña en particular.
Por tanto, no solo nos ocupamos del diseño, sino también de la funcionalidad.
Los patrones como el estilo “fuera del lienzo” no funcionarán aquí, ya que estamos tratando de mostrar la pestaña actual, no de ocultarla. Convirtiendo en un <select>
el menú desplegable no funcionará porque es HTML diferente y JavaScript diferente.
El HTML
Las pestañas son navegación, por lo que <nav>
. El role
debe estar implícito en la etiqueta, pero no siempre se puede contar con eso, por lo que agregamos el role
. Usamos una clase para CSS en el elemento más externo (el <nav>
). Los elementos de navegación en sí están en una lista porque eso es lo mejor. Cada enlace tiene un destino #hash o una URL válida.
<nav role="navigation" class="transformer-tabs">
<ul>
<li><a href="https://css-tricks.com/transformer-tabs/#tab-1">Important Tab</a></li>
<li><a href="#tab-2" class="active">Smurfvision</a></li>
<li><a href="#tab-3">Monster Truck Rally</a></li>
<li><a href="http://google.com">Go To Google →</a></li>
</ul>
</nav>
Nada superfluo.
El CSS de la vista con pestañas
Hay un conjunto de CSS específico para las pestañas en cada “estado” (pestañas o menú desplegable). Puede comenzar con “móvil primero” diseñando el menú desplegable y luego usando la consulta de medios de ancho mínimo para reorganizar para que se vea en pestañas o puede comenzar con “escritorio primero” al diseñar las pestañas primero y luego usar una consulta de medios de ancho máximo para reorganizar en un menú desplegable. Son tan diferentes que no veo ninguna gran ventaja de ninguna manera, pero coincidiría con lo que estás haciendo en otras partes del sitio.
Escritorio primero y SCSS para esta demostración.
.transformer-tabs {
ul {
list-style: none;
padding: 0;
margin: 0;
border-bottom: 3px solid white;
}
li {
display: inline-block;
padding: 0;
vertical-align: bottom;
}
a {
display: inline-block;
color: white;
text-decoration: none;
padding: 0.5rem;
&.active {
border-bottom: 3px solid black;
position: relative;
bottom: -3px;
}
}
}
Solo una fila de enlaces con una línea debajo. El enlace de anclaje con una clase “activa” obtiene un borde de color diferente que se superpone al borde de borde a borde. vertical-align
los mantiene a todos en la misma línea de base cuando el enlace activo obtiene el borde que los demás no tienen.
El CSS de la vista desplegable
Necesitamos encontrar un ancho de ventana gráfica en el que el aspecto con pestañas se descomponga y coloque una consulta de medios allí. 700px para esta demostración.
.transformer-tabs {
...
@media (max-width: 700px) {
ul {
border-bottom: 0;
overflow: hidden;
position: relative;
background: linear-gradient(#666, #222);
&::after {
content: "☰"; /* "Three Line Menu Navicon" shows up */
position: absolute;
top: 8px;
right: 15px;
z-index: 2;
pointer-events: none;
}
}
li {
display: block; /* One link per "row" */
}
a {
position: absolute; /* Stack links on top of each other */
top: 0;
left: 0;
width: 100%;
height: 100%;
&.active {
border: 0;
z-index: 1; /* Active tab is on top */
background: linear-gradient(#666, #222);
}
}
}
}
Cuando llega la consulta de medios, esto nos lleva aquí:
La pestaña activa sigue siendo obvia (es la única que se muestra) y se muestra un menú de navegación de tres líneas, que se conoce universalmente como un símbolo para revelar más navegación.
La estructura de JavaScript
La funcionalidad que necesitamos:
- Si es un enlace #hash, al seleccionarlo, se muestra el panel con esa ID y se oculta el que se muestra actualmente.
- Cambie la URL para indicar que #hash, pero no afecta el historial (sin molestias del botón de retroceso).
- Indique visualmente la pestaña que ahora está activa (alternar las clases de manera apropiada).
- Si es una pestaña vinculada, permita que ese vínculo funcione.
- Si la página se carga con un hash que coincide con una pestaña, vaya a esa pestaña.
- En el estado desplegable para pantallas pequeñas, permita que el menú desplegable se abra / cierre cuando lo seleccione.
Alguna estructura basada en esos requisitos:
var Tabs = {
init: function() {
this.bindUIfunctions();
this.pageLoadCorrectTab();
},
bindUIfunctions: function() {
},
changeTab: function(hash) {
},
pageLoadCorrectTab: function() {
},
toggleMobileMenu: function(event, el) {
}
}
Tabs.init();
Cambio de pestañas tras la selección
La única vez que necesitamos cambiar una pestaña en la selección es cuando la pestaña seleccionada es un enlace #hash. De lo contrario, es una pestaña vinculada y debería seguir ese enlace. (Y por “selección” me refiero a hacer clic o tocar o tabular y activar o lo que sea). Por lo tanto, nuestra función changeTab puede simplemente aceptar ese valor hash y usarlo.
changeTab: function(hash) {
// find the link based on that hash
var anchor = $("[href="https://css-tricks.com/transformer-tabs/+ hash +"]");
// find the related content panel
var div = $(hash);
// activate correct anchor (visually)
anchor.addClass("active").parent().siblings().find("a").removeClass("active");
// activate correct div (visually)
div.addClass("active").siblings().removeClass("active");
// update URL, no history addition
window.history.replaceState("", "", hash);
// Close menu, in case in dropdown state
anchor.closest("ul").removeClass("open");
},
Manejo de clics en las pestañas
Usaremos la delegación de eventos estándar aquí solo para ser eficientes. Cualquier “clic” (que funciona bien para toques), asumiendo que no es la pestaña ya activa y es un enlace #hash, simplemente pasará ese hash a la función changeTab.
// Delegation
$(document)
.on("click", ".transformer-tabs a[href^='#']:not('.active')", function(event) {
Tabs.changeTab(this.hash);
event.preventDefault();
})
… y preventDefault()
para que la página no salte hacia abajo de forma incómoda.
Alternar el menú desplegable
Si la consulta de medios está en vigor y, por lo tanto, las pestañas en su estado desplegable, podemos alternar el menú desplegable “abierto” y “cerrado” cuando el .active
se hace clic en la pestaña. Debido a nuestro estilo, sabemos que la pestaña activa cubre toda el área en la que se puede hacer clic.
$(document)
// ... first click handler, chaining for efficiency
.on("click", ".transformer-tabs a.active", function(event) {
Tabs.toggleMobileMenu(event, this);
event.preventDefault();
});
La función toggleMobileMenu es muy simple. Pero aún me gusta que lo hayamos abstraído en su propia función en caso de que algún día necesite hacer más, no nos estamos poniendo todos los espaguetis.
toggleMobileMenu: function(event, el) {
$(el).closest("ul").toggleClass("open");
}
El .open
class abre visualmente el menú a través de algunos cambios de CSS.
.transformer-tabs {
...
@media (max-width: 700px) {
ul {
...
&.open {
a {
position: relative;
display: block;
}
}
}
}
...
}
Eliminar la posición absoluta en esas pestañas y hacerlas a nivel de bloque hace que el menú se expanda hacia abajo y empuje el contenido hacia abajo también. Esto es lo que lo convierte en un menú desplegable.
Cargue la pestaña correcta cuando la página se cargue con un #hash
Resulta que esto es muy fácil. Solo mire el hash en la URL y páselo a la función changeTab.
pageLoadCorrectTab: function() {
this.changeTab(document.location.hash);
},
Es por eso que abstraemos la funcionalidad en funciones que podemos reutilizar en lugar de spaghetti-land.
Esto no es solo teórico
Estoy escribiendo esto después de arreglar las pestañas en CodePen, donde las pestañas apestaban un poco hasta que hice esto. Tenemos pestañas de enlace #hash y pestañas enlazadas reales en CodePen, de ahí los requisitos.
¿Quieres hacerlo mejor?
Quizás una versión en la que el menú no empuja el contenido de abajo hacia abajo, el menú desplegable expandido solo se encuentra en la parte superior del contenido sería genial. Quizás uno que pueda manejar una tonelada de enlaces (más que el valor de una pantalla pequeña) con algún tipo de desplazamiento o paginación. Quizás uno con algunas animaciones / transiciones.
Un problema con la demostración exacta que hemos creado aquí es que no puede cerrar el menú desplegable a menos que seleccione una pestaña. Puede seleccionar la pestaña actual para cerrarla sin hacer nada, pero no puede simplemente hacer clic en el menú de tres líneas para cerrarla. Eso es porque no puede (hasta donde yo sé) vincular un evento de clic a un pseudo elemento. En CodePen acabo de usar un <span>
para el menú de tres líneas para que pueda alternarlo de esa manera, pero eso significa un marcado adicional.
Manifestación
Nota: “Ir a Google →” es una pestaña vinculada solo para probarlo. No funciona aquí en CodePen debido al iframe de espacio aislado. Funcionaría en circunstancias normales.
Vea las pestañas de Pen Transformer de Chris Coyier (@chriscoyier) en CodePen