如何为子地区分组国家列表添加下拉展开收起及排序筛选功能
实现国家列表的展开收起、排序与筛选功能
需求说明
已完成按子地区分组的国家列表展示,需要新增以下功能:
- 点击子地区标题(如北美),展开/收起下属所有国家
- 点击顶部列标题(如面积),对国家进行排序
- 添加首字母筛选功能,仅显示名称以指定字母开头的国家
修改后的完整代码
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>国家列表</title> <link rel="stylesheet" href="style.css"> </head> <body> <main class="container"> <!-- 首字母筛选栏 --> <div class="filter-bar"> <label for="letter-filter">首字母筛选:</label> <select id="letter-filter"> <option value="">全部</option> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> <option value="D">D</option> <option value="E">E</option> <option value="F">F</option> <option value="G">G</option> <option value="H">H</option> <option value="I">I</option> <option value="J">J</option> <option value="K">K</option> <option value="L">L</option> <option value="M">M</option> <option value="N">N</option> <option value="O">O</option> <option value="P">P</option> <option value="Q">Q</option> <option value="R">R</option> <option value="S">S</option> <option value="T">T</option> <option value="U">U</option> <option value="V">V</option> <option value="W">W</option> <option value="X">X</option> <option value="Y">Y</option> <option value="Z">Z</option> </select> </div> <!-- 列标题(可点击排序) --> <header class="sortable-header"> <div class="header-item" data-sort="name"> <h1>名称</h1> </div> <div class="header-item" data-sort="capital"> <h1>首都</h1> </div> <div class="header-item" data-sort="population"> <h1>人口</h1> </div> <div class="header-item" data-sort="area"> <h1>面积</h1> </div> </header> <!-- 国家列表容器 --> <div id="countries-container"></div> <script src="script.js"></script> </main> </body> </html>
CSS
body { min-height: 100vh; background-color: aliceblue; font-family: 'Open Sans', Arial; font-size: 18px; padding: 2rem; margin: 0; } .container { max-width: 1200px; margin: 0 auto; } /* 筛选栏样式 */ .filter-bar { margin-bottom: 1rem; padding: 0.5rem; background: white; border-radius: 0.5rem; display: flex; align-items: center; gap: 0.5rem; } #letter-filter { padding: 0.3rem 0.6rem; border: 1px solid #ddd; border-radius: 0.3rem; font-size: 1rem; } /* 排序标题样式 */ .sortable-header { display: flex; justify-content: space-between; padding: 1rem; background: white; border-radius: 0.5rem; margin-bottom: 1rem; cursor: pointer; } .header-item { width: 23%; text-align: center; position: relative; } .header-item::after { content: " ↕"; font-size: 0.8rem; margin-left: 0.3rem; } /* 子地区与国家列表样式 */ #countries-container ul { list-style: none; padding: 0; margin: 0; } .subregion { background: white; border-radius: 0.5rem; margin-bottom: 1rem; overflow: hidden; } .subregion-header { display: flex; justify-content: space-between; padding: 1rem 2rem; background: #f0f0f0; cursor: pointer; align-items: center; } .subregion-header h2 { margin: 0; color: #333; } .subregion-stats { display: flex; gap: 2rem; } .subregion-stats h3 { margin: 0; font-size: 1rem; color: #666; } .country-list { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; } .country-list.expanded { max-height: 2000px; } .country { display: flex; justify-content: space-between; padding: 0.8rem 2rem; border-bottom: 1px solid #eee; } .country:last-child { border-bottom: none; } .country > div { width: 23%; text-align: center; } .country h4 { margin: 0; font-size: 0.9rem; color: #333; }
JavaScript
// 全局变量存储原始数据、排序状态、筛选条件 let countriesData = []; let currentSortKey = "name"; let currentSortOrder = "asc"; let currentFilterLetter = ""; // 初始化页面 async function init() { // 获取并处理数据 const res = await fetch("https://restcountries.com/v3.1/all"); const rawData = await res.json(); // 格式化数据,处理缺失的首都/面积/人口 countriesData = rawData.map(country => ({ name: country.name.common, capital: country.capital ? country.capital[0] : "无", population: country.population || 0, area: country.area || 0, subregion: country.subregion || "无所属子地区" })); // 按子地区和名称初始排序 sortCountries("name", "asc"); // 渲染页面 renderCountries(); // 绑定事件 bindEvents(); } // 绑定所有交互事件 function bindEvents() { // 子地区展开收起事件(事件委托) document.getElementById("countries-container").addEventListener("click", (e) => { if (e.target.closest(".subregion-header")) { const subregionItem = e.target.closest(".subregion"); const countryList = subregionItem.querySelector(".country-list"); countryList.classList.toggle("expanded"); } }); // 列标题排序事件 document.querySelectorAll(".header-item").forEach(item => { item.addEventListener("click", () => { const sortKey = item.dataset.sort; // 切换排序顺序 if (currentSortKey === sortKey) { currentSortOrder = currentSortOrder === "asc" ? "desc" : "asc"; } else { currentSortKey = sortKey; currentSortOrder = "asc"; } sortCountries(currentSortKey, currentSortOrder); renderCountries(); }); }); // 首字母筛选事件 document.getElementById("letter-filter").addEventListener("change", (e) => { currentFilterLetter = e.target.value; renderCountries(); }); } // 排序国家数据 function sortCountries(key, order) { countriesData.sort((a, b) => { let valA, valB; // 处理字符串和数字的排序逻辑 if (key === "name" || key === "capital") { valA = a[key].toLowerCase(); valB = b[key].toLowerCase(); } else { valA = a[key]; valB = b[key]; } if (order === "asc") { return valA > valB ? 1 : -1; } else { return valA < valB ? 1 : -1; } }); } // 渲染国家列表 function renderCountries() { const container = document.getElementById("countries-container"); container.innerHTML = ""; // 按子地区分组 const groupedBySubregion = countriesData.reduce((groups, country) => { // 应用首字母筛选 if (currentFilterLetter && !country.name.startsWith(currentFilterLetter)) { return groups; } const subregion = country.subregion; if (!groups[subregion]) { groups[subregion] = []; } groups[subregion].push(country); return groups; }, {}); // 创建列表元素 const ul = document.createElement("ul"); Object.entries(groupedBySubregion).forEach(([subregion, countries]) => { // 计算子地区的总人口和总面积 const totalPopulation = countries.reduce((sum, c) => sum + c.population, 0); const totalArea = countries.reduce((sum, c) => sum + c.area, 0); // 创建子地区项 const subregionLi = document.createElement("li"); subregionLi.className = "subregion"; // 子地区头部 const header = document.createElement("div"); header.className = "subregion-header"; const nameDiv = document.createElement("div"); nameDiv.innerHTML = `<h2>${subregion}</h2>`; const statsDiv = document.createElement("div"); statsDiv.className = "subregion-stats"; statsDiv.innerHTML = ` <h3>总人口: ${formatNumber(totalPopulation)}</h3> <h3>总面积: ${formatNumber(totalArea)} km²</h3> `; header.appendChild(nameDiv); header.appendChild(statsDiv); subregionLi.appendChild(header); // 国家列表 const countryList = document.createElement("div"); countryList.className = "country-list"; const ol = document.createElement("ol"); countries.forEach(country => { const countryLi = document.createElement("li"); countryLi.className = "country"; countryLi.innerHTML = ` <div><h4>${country.name}</h4></div> <div><h4>${country.capital}</h4></div> <div><h4>${formatNumber(country.population)}</h4></div> <div><h4>${formatNumber(country.area)} km²</h4></div> `; ol.appendChild(countryLi); }); countryList.appendChild(ol); subregionLi.appendChild(countryList); ul.appendChild(subregionLi); }); container.appendChild(ul); } // 格式化数字为千分位 function formatNumber(num) { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } // 启动初始化 init();
功能实现说明
1. 子地区展开/收起
- 使用CSS的
max-height和过渡动画实现平滑展开收起效果 - 通过事件委托绑定点击事件,避免为每个子地区单独绑定事件
- 点击子地区头部时,切换
country-list元素的expanded类,控制显示/隐藏
2. 列标题排序
- 为每个列标题添加
data-sort属性,标记排序字段 - 点击标题时切换排序顺序(升序/降序),更新全局排序状态
- 根据排序字段类型(字符串/数字)分别处理排序逻辑,确保排序正确
- 排序后重新渲染整个列表
3. 首字母筛选
- 添加下拉选择框,包含A-Z所有字母和"全部"选项
- 选择字母后,更新全局筛选条件,重新渲染列表时只保留名称以指定字母开头的国家
- 筛选逻辑嵌入到分组渲染过程中,无需额外过滤数组
内容的提问来源于stack exchange,提问作者fantomx775
相关产品推荐
相关产品推荐

