如何在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报错 - 大小写兼容:统一转换为小写
相关产品推荐
相关产品推荐

