búsqueda en lista en tiempo real
Filtra ciudades al instante introduciendo palabras clave y resalta los resultados coincidentes.
Tras activar JavaScript, puedes editar, ejecutar y guardar este ejemplo. El código fuente fijado es el siguiente:
<!doctype html>
<html lang="es" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>búsqueda en lista en tiempo real</title>
<style>
* { box-sizing: border-box; }
body { min-height: 100vh; margin: 0; padding: clamp(28px, 6vw, 70px) 20px; color: #242f35; font-family: "Avenir Next", "Microsoft YaHei", sans-serif; background: #e8f0f0; }
main { width: min(850px, 100%); margin: auto; }
header { margin-bottom: 28px; }
.eyebrow { color: #d25c42; font-size: 11px; font-weight: 900; letter-spacing: .18em; }
h1 { margin: 10px 0 10px; font: 700 clamp(38px, 8vw, 68px)/1 Georgia, "STSong", serif; }
header p { margin: 0; color: #718087; }
.search { display: flex; gap: 10px; padding: 9px; border: 1px solid #cad6d7; border-radius: 17px; background: #fff; box-shadow: 0 16px 38px rgba(46,68,72,.08); }
.search span { display: grid; width: 42px; place-items: center; color: #d25c42; font-size: 22px; }
input { min-width: 0; flex: 1; border: 0; color: #243138; font: inherit; font-size: 17px; outline: 0; }
input::placeholder { color: #9aa7aa; }
.clear { padding: 10px 15px; border: 0; border-radius: 10px; color: #66777c; font: inherit; font-weight: 750; background: #edf2f2; cursor: pointer; }
.meta { display: flex; justify-content: space-between; gap: 16px; margin: 20px 2px 12px; color: #718087; font-size: 12px; }
.meta strong { color: #d25c42; }
.list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
article { display: grid; grid-template-columns: 48px 1fr auto; gap: 14px; align-items: center; padding: 16px; border: 1px solid #d3ddde; border-radius: 16px; background: rgba(255,255,255,.75); transition: transform .18s ease, background .18s ease; }
article:hover { transform: translateY(-3px); background: #fff; }
.code { display: grid; width: 48px; height: 48px; place-items: center; border-radius: 13px; color: #fff; font: 800 12px/1 Georgia, serif; background: var(--color); }
h2 { margin: 0 0 4px; font: 700 19px/1.2 Georgia, "STSong", serif; }
article p { margin: 0; color: #849196; font-size: 12px; }
.temperature { color: #53636a; font-size: 13px; font-weight: 800; }
mark { padding: 1px 2px; color: inherit; background: #ffd7a3; border-radius: 3px; }
.empty { grid-column: 1 / -1; padding: 58px 20px; border: 1px dashed #b9c8ca; border-radius: 18px; color: #718087; text-align: center; }
@media (max-width: 650px) { .list { grid-template-columns: 1fr; } }
@media (max-width: 420px) { article { grid-template-columns: 42px 1fr; } .temperature { grid-column: 2; } }
@media (prefers-reduced-motion: reduce) { article { transition: none; } }
</style>
</head>
<body>
<main>
<header><span class="eyebrow">CITY WEATHER INDEX</span><h1>¿A dónde vas hoy?</h1><p>Busca el nombre de una ciudad, su pinyin o provincia, y la lista se actualizará al instante.</p></header>
<div class="search"><span aria-hidden="true">⌕</span><input id="query" type="search" aria-label="Buscar ciudad" placeholder="Por ejemplo: Hangzhou, chengdu, Guangdong" autocomplete="off"><button id="clear" class="clear" type="button">Borrar</button></div>
<div class="meta"><span>Ciudades coincidentes</span><span><strong id="count">0</strong> results</span></div>
<section id="list" class="list" aria-live="polite"></section>
</main>
<script>
const cities = [
{ name: 'Beijing', pinyin: 'beijing', province: 'Beijing', code: 'BJ', weather: 'soleado', temp: '29°', color: '#d86a45' },
{ name: 'Shanghái', pinyin: 'shanghai', province: 'Shanghái', code: 'SH', weather: 'Muy nublado', temp: '31°', color: '#547c99' },
{ name: 'Hangzhou', pinyin: 'hangzhou', province: 'Zhejiang', code: 'HZ', weather: 'Chubascos', temp: '28°', color: '#4d8b72' },
{ name: 'Chengdú', pinyin: 'chengdu', province: 'Sichuan', code: 'CD', weather: 'Yin', temp: '26°', color: '#8a7753' },
{ name: 'Xiamen', pinyin: 'xiamen', province: 'Fujian', code: 'XM', weather: 'soleado', temp: '30°', color: '#3e8e9b' },
{ name: 'Qingdao', pinyin: 'qingdao', province: 'Shandong', code: 'QD', weather: 'Brisa marina', temp: '25°', color: '#4672a1' },
{ name: 'Guangzhou', pinyin: 'guangzhou', province: 'Guangdong', code: 'GZ', weather: 'Tormenta y lluvia', temp: '32°', color: '#9b5e70' },
{ name: 'Dali', pinyin: 'dali', province: 'Yunnan', code: 'DL', weather: 'soleado', temp: '23°', color: '#687a43' }
];
const queryInput = document.querySelector('#query');
const list = document.querySelector('#list');
const count = document.querySelector('#count');
function appendHighlighted(target, text, query) {
const index = text.toLocaleLowerCase().indexOf(query);
if (!query || index < 0) { target.textContent = text; return; }
target.append(document.createTextNode(text.slice(0, index)));
const mark = document.createElement('mark');
mark.textContent = text.slice(index, index + query.length);
target.append(mark, document.createTextNode(text.slice(index + query.length)));
}
function createCard(city, query) {
const article = document.createElement('article');
const code = document.createElement('span'); code.className = 'code'; code.style.setProperty('--color', city.color); code.textContent = city.code;
const info = document.createElement('div');
const title = document.createElement('h2'); appendHighlighted(title, city.name, query);
const detail = document.createElement('p'); appendHighlighted(detail, `${city.province} · ${city.pinyin} · ${city.weather}`, query);
const temp = document.createElement('span'); temp.className = 'temperature'; temp.textContent = city.temp;
info.append(title, detail); article.append(code, info, temp); return article;
}
function render() {
const query = queryInput.value.trim().toLocaleLowerCase();
const matches = cities.filter(city => `${city.name} ${city.pinyin} ${city.province}`.toLocaleLowerCase().includes(query));
list.replaceChildren(...matches.map(city => createCard(city, query)));
count.textContent = String(matches.length);
if (!matches.length) { const empty = document.createElement('p'); empty.className = 'empty'; empty.textContent = 'No se encontró ninguna ciudad coincidente; prueba con una palabra clave más corta.'; list.append(empty); }
}
queryInput.addEventListener('input', render);
document.querySelector('#clear').addEventListener('click', () => { queryInput.value = ''; queryInput.focus(); render(); });
render();
</script>
</body>
</html>