卡片、列表与内容组织

实时列表搜索

输入关键词即时筛选城市,并高亮匹配结果。

HTMLCSSJavaScript搜索
启用 JavaScript 后可编辑、运行和保存此案例。固定源码如下:
<!doctype html>
<html lang="zh-Hans" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>实时列表搜索</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>今天去哪里?</h1><p>搜索城市名、拼音或省份,列表会即时更新。</p></header>
    <div class="search"><span aria-hidden="true">⌕</span><input id="query" type="search" aria-label="搜索城市" placeholder="例如:杭州、chengdu、广东" autocomplete="off"><button id="clear" class="clear" type="button">清空</button></div>
    <div class="meta"><span>匹配城市</span><span><strong id="count">0</strong> 个结果</span></div>
    <section id="list" class="list" aria-live="polite"></section>
  </main>
  <script>
    const cities = [
      { name: '北京', pinyin: 'beijing', province: '北京', code: 'BJ', weather: '晴朗', temp: '29°', color: '#d86a45' },
      { name: '上海', pinyin: 'shanghai', province: '上海', code: 'SH', weather: '多云', temp: '31°', color: '#547c99' },
      { name: '杭州', pinyin: 'hangzhou', province: '浙江', code: 'HZ', weather: '阵雨', temp: '28°', color: '#4d8b72' },
      { name: '成都', pinyin: 'chengdu', province: '四川', code: 'CD', weather: '阴', temp: '26°', color: '#8a7753' },
      { name: '厦门', pinyin: 'xiamen', province: '福建', code: 'XM', weather: '晴朗', temp: '30°', color: '#3e8e9b' },
      { name: '青岛', pinyin: 'qingdao', province: '山东', code: 'QD', weather: '海风', temp: '25°', color: '#4672a1' },
      { name: '广州', pinyin: 'guangzhou', province: '广东', code: 'GZ', weather: '雷雨', temp: '32°', color: '#9b5e70' },
      { name: '大理', pinyin: 'dali', province: '云南', code: 'DL', weather: '晴朗', 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 = '没有找到匹配城市,试试更短的关键词。'; list.append(empty); }
    }
    queryInput.addEventListener('input', render);
    document.querySelector('#clear').addEventListener('click', () => { queryInput.value = ''; queryInput.focus(); render(); });
    render();
  </script>
</body>
</html>
页面反馈发现问题或有改进建议?
反馈功能需要在在线版使用

请在在线版启用 JavaScript 后提交;当前工具或案例的本地功能不受影响。

前往在线版反馈