Cards, Lists, and Content Organization

Live list search

Type keywords to filter cities instantly and highlight matching results.

HTMLCSSJavaScriptSearch
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Live list search</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>Where are we going today?</h1><p>Search by city name, pinyin, or province, and the list updates instantly.</p></header>
    <div class="search"><span aria-hidden="true">⌕</span><input id="query" type="search" aria-label="Search cities" placeholder="For example: Hangzhou, chengdu, Guangdong" autocomplete="off"><button id="clear" class="clear" type="button">Clear</button></div>
    <div class="meta"><span>Matched Cities</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: 'Sunny', temp: '29°', color: '#d86a45' },
      { name: 'Shanghai', pinyin: 'shanghai', province: 'Shanghai', code: 'SH', weather: 'Cloudy', temp: '31°', color: '#547c99' },
      { name: 'Hangzhou', pinyin: 'hangzhou', province: 'Zhejiang', code: 'HZ', weather: 'Showers', temp: '28°', color: '#4d8b72' },
      { name: 'Chengdu', pinyin: 'chengdu', province: 'Sichuan', code: 'CD', weather: 'Yin', temp: '26°', color: '#8a7753' },
      { name: 'Xiamen', pinyin: 'xiamen', province: 'Fujian', code: 'XM', weather: 'Sunny', temp: '30°', color: '#3e8e9b' },
      { name: 'Qingdao', pinyin: 'qingdao', province: 'Shandong', code: 'QD', weather: 'Sea Breeze', temp: '25°', color: '#4672a1' },
      { name: 'Guangzhou', pinyin: 'guangzhou', province: 'Guangdong', code: 'GZ', weather: 'Thunderstorm', temp: '32°', color: '#9b5e70' },
      { name: 'Dali', pinyin: 'dali', province: 'Yunnan', code: 'DL', weather: 'Sunny', 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 matching city found, try a shorter keyword.'; list.append(empty); }
    }
    queryInput.addEventListener('input', render);
    document.querySelector('#clear').addEventListener('click', () => { queryInput.value = ''; queryInput.focus(); render(); });
    render();
  </script>
</body>
</html>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback