Table of Contents
Introducción
Construir una galería de fotos dinámica con capacidades de filtrado y clasificación transforma una colección de imágenes estática en una experiencia atractiva y fácil de usar. Los visitantes pueden encontrar rápidamente imágenes que se ajusten a sus intereses, por ejemplo, filtrando por naturaleza, ciudad o gente, y reordenar resultados por nombre, fecha o calificación sin una recarga de página. Este artículo proporciona un enfoque completo y listo para la producción para crear una galería usando simple JavaScript, HTML y CSS.
Configuración de la estructura HTML
Una fundación HTML bien estructurada hace que el filtrado y la clasificación sea más directo. Cada elemento de imagen debe llevar atributos personalizados que describen su categoría, nombre, fecha, o cualquier otra propiedad que usted tiene la intención de filtrar o ordenar.
Marcado de base para filtros y controles de clasificación
Crear un contenedor para botones de filtro y otro para clasificar controles. Use elementos con y atributos:
<div id="controls">
<div id="filter-buttons" role="group" aria-label="Filter photos by category">
<button data-filter="all" class="active">Show All</button>
<button data-filter="nature">Nature</button>
<button data-filter="city">City</button>
<button data-filter="people">People</button>
</div>
<div id="sort-options" role="group" aria-label="Sort photos">
<button data-sort="name">Sort by Name</button>
<button data-sort="date">Sort by Date</button>
<button data-sort="rating">Sort by Rating</button>
</div>
</div>
Incluye ] y mejora la accesibilidad para los usuarios de lectores de pantalla. El botón está marcado con una clase para indicar el estado inicial; lo va a hacer con CSS.
Galería de imágenes Container
En el interior de un ], coloca cada foto en su propio elemento de envoltura. Usa , , , y atributos para filtrar y clasificar. Por ejemplo:
<div id="gallery" class="photo-grid">
<div class="photo" data-category="nature" data-name="Sunset Over the Hills" data-date="2023-06-15" data-rating="4.5">
<img src="images/sunset.jpg" alt="Sunset over rolling hills" loading="lazy">
<div class="photo-caption">Sunset Over the Hills</div>
</div>
<div class="photo" data-category="city" data-name="Downtown Skyline" data-date="2023-07-01" data-rating="4.2">
<img src="images/skyline.jpg" alt="Downtown skyline at dusk" loading="lazy">
<div class="photo-caption">Downtown Skyline</div>
</div>
<!-- more photos -->
</div>
El atributo aplaza la carga de imágenes de pantalla apagada, mejorando el rendimiento de la carga de página inicial. Cada envoltura de fotos también puede incluir una tapa o superposición para información adicional.
Utilizando los Atributos de Datos de manera efectiva
Los atributos de datos ] (por ejemplo, , ) no se limitan a cadenas. Para clasificar numéricamente, almacenar números como cadenas; JavaScript los analizará según sea necesario. Para múltiples categorías, puede almacenar una lista separada como y dividirla durante la técnica de filtración.
Estilizar la galería con CSS
Un diseño limpio y receptivo establece el escenario para las características interactivas. Use CSS Grid o Flexbox para la rejilla de las fotos, y estilo los botones para indicar filtros activos y estado de clasificación.
Foto Grid Layout
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
padding: 1rem 0;
}
.photo {
position: relative;
overflow: hidden;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.photo img {
width: 100%;
height: auto;
display: block;
transition: transform 0.3s ease;
}
.photo:hover img {
transform: scale(1.05);
}
Esta cuadrícula garantiza que las imágenes se adapten al tamaño de la pantalla. El efecto del audífono añade una interacción sutil del zoom, haciendo que la galería se sienta más pulida.
Activo para los botones
#filter-buttons button.active,
#sort-options button.active {
background-color: #007acc;
color: #fff;
border-color: #007acc;
}
button {
padding: 0.5rem 1rem;
border: 1px solid #ccc;
background: #fff;
cursor: pointer;
margin: 0.25rem;
border-radius: 4px;
transition: background 0.2s, color 0.2s;
}
button:hover {
background: #e6e6e6;
}
Aplicar la clase al botón de filtro/sorte actual proporciona retroalimentación visual. JavaScript gestionará la adición y eliminación de esta clase.
Consideraciones responsivas
Asegurar que la galería funciona en dispositivos táctiles. Aumente los tamaños de los objetivos de contacto de botón a al menos 44px por 44px (WCAG 2.1 recomendación). Utilice las consultas de medios si es necesario para cambiar de una cuadrícula multicolor a una sola columna en pantallas muy pequeñas.
Implementación de Filtro de JavaScript
Ahora traemos la galería a la vida. La idea central: escuchar por clics en los botones de filtro, luego mostrar o ocultar elementos de foto basados en su atributo .
Selección de elementos de la DOM y inicialización
const filterButtons = document.querySelectorAll('#filter-buttons button');
const sortButtons = document.querySelectorAll('#sort-options button');
const gallery = document.getElementById('gallery');
let photos = Array.from(gallery.children);
let activeFilter = 'all';
let activeSort = 'name'; // default sort
El almacenamiento como un array nos da fácil acceso a métodos de matriz como y . También mantenemos un seguimiento del filtro actual y orden de orden para que podamos volver a solicitar la clasificación después de un cambio de filtro.
Filtros Logic
function filterPhotos(filter) {
activeFilter = filter;
photos.forEach(photo => {
const categories = photo.dataset.category; // may be a space-separated string
const shouldShow = filter === 'all' || categories.split(' ').includes(filter);
photo.style.display = shouldShow ? '' : 'none';
});
// After filtering, re-sort the visible photos
sortPhotos(activeSort);
}
Esta función soporta múltiples categorías: si , entonces elegir “naturaleza” o “paisaje” mostrará la foto. Nota: ] devuelve el valor de (como cadena). Usar maneja el caso de multicategoría.
Adjuntar a los oyentes de eventos para filtrar los botones
filterButtons.forEach(button => {
button.addEventListener('click', () => {
const filter = button.dataset.filter;
// Update active class on filter buttons
filterButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
// Apply filter
filterPhotos(filter);
});
});
Eliminar la clase de todos los botones y añadirla al clic garantiza la consistencia visual. La función se llama con el valor de filtro seleccionado.
Implementación de la clasificación de JavaScript
Ordenar reorganiza las fotos visibles en el DOM basado en un atributo elegido (por ejemplo, nombre, fecha, calificación).
Función de clasificación
function sortPhotos(sortBy) {
activeSort = sortBy;
// Get currently visible photos (display not 'none')
const visiblePhotos = photos.filter(photo => photo.style.display !== 'none');
visiblePhotos.sort((a, b) => {
let valA, valB;
switch (sortBy) {
case 'name':
valA = a.dataset.name.toLowerCase();
valB = b.dataset.name.toLowerCase();
return valA.localeCompare(valB);
case 'date':
valA = new Date(a.dataset.date);
valB = new Date(b.dataset.date);
return valA - valB;
case 'rating':
valA = parseFloat(a.dataset.rating);
valB = parseFloat(b.dataset.rating);
return valB - valA; // descending: highest first
default:
return 0;
}
});
// Re‑append sorted photos back to the gallery container
visiblePhotos.forEach(photo => gallery.appendChild(photo));
}
La clasificación funciona sólo en fotos visibles, lo que significa después de un cambio de filtro que restaura el orden según el tipo activo. Para cadenas, utilizamos para el correcto orden alfabético. Las fechas se comparan como objetos Fecha, y las calificaciones se clasifican en orden descendente (más popular primero).
Escuchares de eventos para botones de tipo
sortButtons.forEach(button => {
button.addEventListener('click', () => {
const sortBy = button.dataset.sort;
sortButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
sortPhotos(sortBy);
});
});
Cuando se hace clic en un botón de tipo, actualizamos la clase activa y llamamos con la tecla de tipo deseada. Porque ya llama después de filtrar, la galería sigue siendo consistente.
Configuración inicial
// Set default active buttons
document.querySelector('#filter-buttons [data-filter="all"]').classList.add('active');
document.querySelector('#sort-options [data-sort="name"]').classList.add('active');
// Load and initially sort by name
filterPhotos('all');
Llamando al principio garantiza que la galería se pobla con todas las imágenes y se clasifica por el tipo predeterminado (nombre).
Filtro avanzado y búsqueda
Un filtro de categoría simple es útil, pero puede que desee combinar varios filtros o añadir un entrada de búsqueda de texto. Las siguientes mejoras se basan en la lógica básica.
Filtros de categoría múltiple (Boletos de verificación)
En lugar de botones exclusivos, utilice las casillas de verificación para que los usuarios puedan seleccionar varias categorías:
<div id="filter-checkboxes">
<label><input type="checkbox" value="nature" checked> Nature</label>
<label><input type="checkbox" value="city" checked> City</label>
<label><input type="checkbox" value="people" checked> People</label>
</div>
Luego, modifique la lógica de filtrado para construir un conjunto de categorías activas:
function filterPhotosByCheckboxes() {
const checked = document.querySelectorAll('#filter-checkboxes input:checked');
const activeCategories = Array.from(checked).map(cb => cb.value);
photos.forEach(photo => {
const photoCategories = photo.dataset.category.split(' ');
const shouldShow = activeCategories.length === 0 ||
photoCategories.some(cat => activeCategories.includes(cat));
photo.style.display = shouldShow ? '' : 'none';
});
sortPhotos(activeSort);
}
Si no se revisan las casillas de verificación, es posible que elija mostrar todas las imágenes (como en el código anterior) o mostrar ninguna, defina el UX deseado.
Entrada de búsqueda de texto
Agregue un campo de entrada que busque capciones o el atributo :
<input type="text" id="search" placeholder="Search photos...">
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', () => {
const query = searchInput.value.toLowerCase();
photos.forEach(photo => {
const name = photo.dataset.name.toLowerCase();
// Also search in caption if present
const caption = photo.querySelector('.photo-caption')?.innerText.toLowerCase() || '';
const matches = name.includes(query) || caption.includes(query);
// If a filter is active, also check category
const filter = activeFilter;
const catMatch = filter === 'all' || photo.dataset.category.split(' ').includes(filter);
photo.style.display = (matches && catMatch) ? '' : 'none';
});
sortPhotos(activeSort);
});
Combine la búsqueda con botones de filtro para permitir el estrechamiento de resultados más allá. Tenga en cuenta que necesita almacenar el estado de filtro actual para respetarlo durante la búsqueda.
Rendimiento y accesibilidad
Una galería dinámica debe ser rápida y utilizable por todos, incluyendo personas que utilizan tecnologías de ayuda.
Lazy Cargando imágenes
Ya usamos que se pospone a la carga hasta que la imagen esté cerca del puerto de visión. Para los navegadores más antiguos, puede implementar un retroceso de carga de JavaScript cargado con la API de Intersección . Esto asegura que incluso si la carga de perezosos nativos no es compatible, las imágenes de pantalla no se cargan, reduciendo ancho de banda y mejorando el rendimiento.
Debouncing Search Input
Si adjuntas al oyente de búsqueda a , se dispara en cada pulsador de teclas. Para grandes galerías, considere la posibilidad de repudiar el manejador (por ejemplo, 200 ms de retraso) para evitar manipulaciones DOM excesivas. Una simple utilidad de desprestigio:
function debounce(func, delay) { let timeoutId; return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } searchInput.addEventListener('input', debounce(handleSearch, 200));Keyboard Navigation
Filter and sort buttons should be focusable and operable via keyboard. Using native
<button>elements already provides keyboard interaction (Enter/Space). Ensure that after filtering or sorting, focus is managed logically; for example, you could move focus to the first photo after a filter change via a focusable element. Also, consider addingaria-pressedattributes to filter buttons to indicate toggle state.Announcing Changes to Screen Readers
Use an ARIA live region to announce filter and sort results:
<div id="gallery-announce" aria-live="polite" class="sr-only"></div>Después de cada acción de filtro/club, actualice el texto de ese elemento, por ejemplo, “Mostrar 8 fotos de naturaleza clasificadas por nombre”. Esto ayuda a los usuarios que confían en los lectores de pantalla a entender lo que cambió.
CSS para contenido oculto
En lugar de usar solo, considere utilizar una clase CSS como que esconde el elemento y lo elimina del árbol de accesibilidad:
.hidden { display: none !important; visibility: hidden; }Usar elimina elementos del árbol de accesibilidad, que es apropiado. Sólo asegúrese de que no confía solamente en sin porque eso deja el elemento que ocupa el espacio.
Conclusión
Combinando HTML semántico limpio, CSS receptivo y JavaScript bien estructurado, puedes construir una galería de fotos dinámica que sea rápida, accesible y agradable de usar. Las técnicas cubiertas —filtrando por categoría (incluyendo varias categorías), clasificando por diversos atributos, agregando búsqueda de texto y optimizando el rendimiento— proporcionan una base sólida que se puede ampliar con características como paginación, desplazamiento infinito o integración con una API de backend.
Para más lectura, la documentación MDN en Array.prototype.sort() explica el algoritmo de clasificación, y la guía de diseño receptiva web.dev ofrece consejos de diseño adicionales. Experimento con diferentes atributos de datos, animaciones y estilo personalizado para adaptar la galería a las necesidades de su proyecto pone un componente de navegación.