You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中实现按国家名称/首都筛选分组数据列表?

问题

现有一个按次区域分组的国家信息列表,已实现排序和折叠展示功能,需要添加筛选功能:根据用户输入的关键词,仅显示国家名称或首都包含该关键词的条目(不区分大小写)。例如输入ab时,展示所有名称或首都含ab的国家。

原代码如下:

HTML代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <main class="container">
        <header>
            <div id="name">
                <h1>Name</h1>
            </div>
            <div id="capital">
                <h1>Capital</h1>
            </div>
            <div id="population">
                <h1>Population</h1>
            </div>
            <div id="area">
                <h1>Area</h1>
            </div>

        </header>

      
      <div id="container"></div>
      <div id="subRegionWrapper"> </div>
      <div id="listWrapper"></div>
      <script src="script.js"></script>
    </main>
</body>

</html>

CSS代码

*{
  transition: 500ms;

}
body {
  margin: 0 15%;
  min-height: 100vh;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: aliceblue;
  font-family: 'Open Sans', Arial;
  font-size: 18px;
}

#name {
  padding: 0 20px;
  background-color: rgb(219, 219, 219);
}

#capital {
  padding: 0 20px;
  background-color: rgb(219, 219, 219);
}

#population {
  padding: 0 20px;
  background-color: rgb(219, 219, 219);
}

#area {
  padding: 0 20px;
  background-color: rgb(219, 219, 219);
}

header {
  margin: 0 10%;
  display: flex;
  justify-content: space-between;
  padding: 22px 0;
  color: rgb(5, 5, 5);
}

ul {
  list-style: none;
  list-style-type: none;
  outline: 2px solid #ddd;
  padding: 1rem 2rem;
  border-radius: 0.5rem;
  list-style-position: inside;
  color: blue;
}

ul ol {
  color: rgb(197, 105, 18);
  list-style: none;
  list-style-type: none;
  font-size: .9em;
  margin: 0.4rem 0;
}

.country {
  display: flex;
  justify-content: space-between;
}

.disp-flex {
  display: flex;
  justify-content: space-between;
}

.disp-flex>div {
  width: 23%;
  padding: 15px 0px;
}


.subregion-header>div:nth-child(1) {
  position: relative;
  left: 30px;
}

.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
  margin: 15px 2px;
}

.accordion li {
  list-style-type: none;
}

.active,
.accordion:hover {
  background-color: #ccc;
}

.panel {
  margin-left: 5%;
  display: none;
  background-color: white;
  overflow: hidden;
}

#name:hover {
  background-color: rgb(114, 114, 114);
  cursor: pointer;
}
#capital:hover {
  background-color: rgb(114, 114, 114);
  cursor: pointer;
}
#population:hover {
  background-color: rgb(114, 114, 114);
  cursor: pointer;
}
#area:hover {
  background-color: rgb(114, 114, 114);
  cursor: pointer;
}

JavaScript代码

let div = document.createElement('div');
let ul = document.createElement('ul');

const data = {};

const el = document.getElementById("name");

sortName = 0;
sortCapital = 0;
sortPopulation = 0;
sortArea = 0;


function sortByArea(data) {
  var child = container.lastElementChild;
  while (child) {
    container.removeChild(child);
    child = container.lastElementChild;
  }
  if(sortArea == 0){
    data.sort((a, b) => {
      if (a.area > b.area) return 1;
      else return -1
    });
    return data;
  }
  data.sort((a, b) => {
    if (a.area < b.area) return 1;
    else return -1
  });
  return data;
}

function sortByPopulation(data) {
  var child = container.lastElementChild;
  while (child) {
    container.removeChild(child);
    child = container.lastElementChild;
  }
  if(sortPopulation == 0){
    data.sort((a, b) => {
      if (a.population > b.population) return 1;
      else return -1
    });
    return data;
  }
  data.sort((a, b) => {
    if (a.population < b.population) return 1;
    else return -1
  });
  return data;
}

function sortByCapital(data) {
  var child = container.lastElementChild;
  while (child) {
    container.removeChild(child);
    child = container.lastElementChild;
  }
  if(sortCapital == 0){
    data.sort((a, b) => {
      if (a.capital > b.capital) return 1;
      else return -1
    });
    return data;
  }
  data.sort((a, b) => {
    if (a.capital < b.capital) return 1;
    else return -1
  });
  return data;
}

function sortByName(data) {
  var child = container.lastElementChild;
  while (child) {
    container.removeChild(child);
    child = container.lastElementChild;
  }
  if(sortName == 0){
    data.sort((a, b) => {
      if (a.name > b.name) return 1;
      else return -1
    });
    return data;
  }
  data.sort((a, b) => {
    if (a.name < b.name) return 1;
    else return -1
  });
  return data;
}
document.getElementById('area').addEventListener('click', () => f(4));
document.getElementById('population').addEventListener('click', () => f(3));
document.getElementById('capital').addEventListener('click', () => f(2));
document.getElementById('name').addEventListener('click', () => f(1));

div.appendChild(ul);
async function f(e) {
  //fetching and sorting data by regions and subregions
  const res = await fetch("https://restcountries.com/v3.1/all");
  var data = await res.json();

  const container = document.getElementById('container');
  const accordion = document.createElement('div');
  const olWrapper = document.getElementById('listWrapper');
  const subRegionWrapper = document.getElementById('subRegionWrapper');

  switch (e) {
    case 1:
      data = sortByName(data);
      sortName += 1;
      sortName %= 2;
    case 2:
      data = sortByCapital(data);
      sortCapital += 1;
      sortCapital %= 2;
    case 3:
      data = sortByPopulation(data);
      sortPopulation += 1;
      sortPopulation %= 2;
    case 4:
      data = sortByArea(data);
      sortArea += 1;
      sortArea %= 2;
  }

  data.sort((a, b) => {
    if (a.region > b.region) return 1;
    else if (a.region < b.region) return -1
    else {
      if (a.subregion > b.subregion) return 1;
      else return -1;
    }
  });

  const subRegions = data.reduce((r, a) => {
    r[a.subregion] = r[a.subregion] || [];
    r[a.subregion].push(a);
    return r;
  }, {});

  const dropdownValues = Object.entries(subRegions);
  dropdownValues.forEach(subRegion => {
    const accordionWrapper = document.createElement('div');
    const panel = document.createElement('div');
    panel.classList.add('panel');
    accordionWrapper.classList.add('accordion');

    const totalArea = subRegion[1].reduce((acc, curr) => acc + curr.area, 0);
    const totalPopulation = subRegion[1].reduce((acc, curr) => acc + curr.population, 0);
    const li = createSubregion(subRegion[0], totalPopulation, totalArea);

    accordionWrapper.appendChild(li);
    accordion.appendChild(accordionWrapper);
    subRegion[1].forEach(item => {
      const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
      const subOl = document.createElement('ol');
      subOl.appendChild(subLi);
      panel.appendChild(subOl);
      accordion.appendChild(panel);
    });

    accordionWrapper.addEventListener('click', function () {
      this.classList.toggle("active");
      const panel = this.nextElementSibling;
      if (panel.style.display === "block") {
        panel.style.display = "none";
      } else {
        panel.style.display = "block";
      }
    });
  });

  container.appendChild(accordion);
}

function createSubregion(name, population, area) {
  var li = document.createElement("li");
  li.setAttribute("class", "subregion");

  var header = document.createElement("div");
  header.setAttribute("class", "subregion-header disp-flex");

  var nameDiv = document.createElement("div");

  var nameh2 = document.createElement("h2");
  nameh2.innerText = name;

  nameDiv.appendChild(nameh2);
  header.append(nameDiv);

  var emptyDiv = document.createElement("div");
  header.appendChild(emptyDiv);

  var populationDiv = document.createElement("div");
  var populationh2 = document.createElement("h3");
  populationh2.innerText = population;

  populationDiv.appendChild(populationh2);
  header.append(populationDiv);

  var areaDiv = document.createElement("div");
  var areah2 = document.createElement("h3");
  areah2.innerText = area;

  areaDiv.appendChild(areah2);
  header.append(areaDiv);

  li.appendChild(header);
  return li;
}

function createCountry(name, capital, area, population) {
  var country = document.createElement("li");
  country.setAttribute("class", "country disp-flex")

  var namediv = document.createElement("div");
  var nameh4 = document.createElement("h4");
  nameh4.innerText = name;
  namediv.appendChild(nameh4);
  country.appendChild(namediv);

  var capitaldiv = document.createElement("div");
  var capitalh4 = document.createElement("h4");
  capitalh4.innerText = capital;
  capitaldiv.appendChild(capitalh4);
  country.appendChild(capitaldiv);

  var popdiv = document.createElement("div");
  var poph4 = document.createElement("h4");
  poph4.innerText = population;
  popdiv.appendChild(poph4);
  country.appendChild(popdiv);

  var areadiv = document.createElement("div");
  var areah4 = document.createElement("h4");
  areah4.innerText = area;
  areadiv.appendChild(areah4);
  country.appendChild(areadiv);


  return country;
}

f(0);
实现筛选功能的步骤

1. 添加筛选输入框到HTML

在header标签之后、#container之前插入搜索输入框,用于用户输入关键词:

<div class="search-wrapper" style="margin: 0 10%; padding: 10px 0;">
  <input type="text" id="searchInput" placeholder="输入关键词筛选国家/首都..." style="width: 100%; padding: 8px; font-size: 16px;">
</div>

2. 改造JavaScript逻辑

2.1 全局存储原始数据

避免重复请求接口,将首次获取的国家数据存储在全局变量中:

// 全局变量存储原始国家数据
let originalData = [];

async function f(e) {
  // 仅首次加载时请求数据
  if (originalData.length === 0) {
    const res = await fetch("https://restcountries.com/v3.1/all");
    originalData = await res.json();
  }
  // 使用原始数据的副本处理,避免污染源数据
  let data = [...originalData];
  
  // 原有排序逻辑保持不变...
}

2.2 新增筛选函数

编写过滤逻辑,匹配国家名称或首都包含关键词的条目(不区分大小写):

function filterData(data, keyword) {
  if (!keyword.trim()) return data; // 关键词为空时返回全部数据
  const lowerKeyword = keyword.toLowerCase();
  return data.filter(country => {
    const countryName = country.name.common.toLowerCase();
    // 部分国家无首都,做容错处理
    const countryCapital = country.capital ? country.capital[0].toLowerCase() : '';
    return countryName.includes(lowerKeyword) || countryCapital.includes(lowerKeyword);
  });
}

2.3 绑定输入事件,实时触发筛选

给搜索框添加输入监听,每次输入时重新渲染列表,同时保留当前排序状态:

// 获取搜索输入框元素
const searchInput = document.getElementById('searchInput');

// 监听输入事件,实时更新列表
searchInput.addEventListener('input', () => {
  // 识别当前排序状态,保持排序不变
  const currentSort = sortName === 1 ? 1 : sortCapital === 1 ? 2 : sortPopulation === 1 ? 3 : sortArea === 1 ? 4 : 0;
  f(currentSort);
});

async function f(e) {
  if (originalData.length === 0) {
    const res = await fetch("https://restcountries.com/v3.1/all");
    originalData = await res.json();
  }
  let data = [...originalData];

  // 原有排序逻辑保持不变...

  // 插入筛选逻辑
  const keyword = searchInput.value;
  data = filterData(data, keyword);

  // 原有分组、渲染逻辑保持不变...
}

2.4 优化分组渲染,过滤空次区域

筛选后部分次区域可能没有符合条件的国家,需要跳过这些空分组:

const dropdownValues = Object.entries(subRegions);
dropdownValues.forEach(subRegion => {
  // 过滤当前次区域下符合条件的国家
  const filteredCountries = filterData(subRegion[1], searchInput.value);
  if (filteredCountries.length === 0) return; // 无符合条件的国家则不渲染该分组

  const accordionWrapper = document.createElement('div');
  const panel = document.createElement('div');
  panel.classList.add('panel');
  accordionWrapper.classList.add('accordion');

  // 基于筛选后的数据重新计算次区域的总人口和总面积
  const totalArea = filteredCountries.reduce((acc, curr) => acc + curr.area, 0);
  const totalPopulation = filteredCountries.reduce((acc, curr) => acc + curr.population, 0);
  const li = createSubregion(subRegion[0], totalPopulation, totalArea);

  accordionWrapper.appendChild(li);
  accordion.appendChild(accordionWrapper);
  
  // 仅渲染筛选后的国家条目
  filteredCountries.forEach(item => {
    const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
    const subOl = document.createElement('ol');
    subOl.appendChild(subLi);
    panel.appendChild(subOl);
    accordion.appendChild(panel);
  });

  // 原有折叠事件逻辑保持不变...
});

3. 细节优化

  • 容错处理:针对无首都的国家,避免出现undefined报错
  • 大小写兼容:统一转换为小写
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 00:50:24