如何在JavaScript中实现多列切换升降序的国家数据排序?
问题修复:实现单列切换排序与多列组合排序
核心问题梳理
原代码存在以下关键问题导致排序功能失效:
- switch语句未添加
break,点击单个表头会触发所有后续case的排序逻辑,最终仅保留最后一次排序结果 - 每次点击都重新请求API数据,未缓存原始数据,无法维护排序状态
- 用户自定义排序后,又执行了region/subregion的强制排序,覆盖了用户选择的排序规则
- 缺乏多列排序的状态管理机制,无法记录多列排序的优先级与方向
修复后的完整代码
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 originalData = []; // 排序状态数组:记录排序字段、方向(1升序,-1降序)、优先级 let sortStates = [ { field: 'name', direction: 1, active: false }, { field: 'capital', direction: 1, active: false }, { field: 'population', direction: 1, active: false }, { field: 'area', direction: 1, active: false } ]; const container = document.getElementById('container'); // 初始化:获取数据并渲染 async function init() { const res = await fetch("https://restcountries.com/v3.1/all"); originalData = await res.json(); // 处理部分国家无capital的情况 originalData.forEach(country => { country.capital = country.capital ? country.capital[0] : '无首都'; }); renderData(); } // 生成组合排序的比较器 function getSortComparator() { // 筛选出激活的排序规则,按点击顺序(优先级)排序 const activeSorts = sortStates.filter(s => s.active); // 如果没有激活的排序,默认按subregion分组排序 if (activeSorts.length === 0) { return (a, b) => { if (a.region > b.region) return 1; if (a.region < b.region) return -1; if (a.subregion > b.subregion) return 1; return -1; }; } // 组合排序逻辑:按优先级依次比较 return (a, b) => { for (const sort of activeSorts) { let valA, valB; switch (sort.field) { case 'name': valA = a.name.common.toLowerCase(); valB = b.name.common.toLowerCase(); break; case 'capital': valA = a.capital.toLowerCase(); valB = b.capital.toLowerCase(); break; case 'population': valA = a.population; valB = b.population; break; case 'area': valA = a.area || 0; // 处理无面积数据的国家 valB = b.area || 0; break; } if (valA > valB) return sort.direction; if (valA < valB) return -sort.direction; // 相等则继续比较下一个排序字段 } // 所有排序字段都相等,按subregion兜底 if (a.subregion > b.subregion) return 1; return -1; }; } // 更新排序状态 function updateSortState(field) { const sortItem = sortStates.find(s => s.field === field); if (!sortItem) return; if (sortItem.active) { // 切换方向 sortItem.direction *= -1; } else { // 激活该排序,添加到优先级队列末尾 sortItem.active = true; } } // 渲染数据 function renderData() { // 清空容器 while (container.lastElementChild) { container.removeChild(container.lastElementChild); } // 复制数据并排序 const sortedData = [...originalData].sort(getSortComparator()); // 按subregion分组 const subRegions = sortedData.reduce((r, a) => { r[a.subregion] = r[a.subregion] || []; r[a.subregion].push(a); return r; }, {}); const accordion = document.createElement('div'); 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), 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 || 0, 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; panel.style.display = panel.style.display === "block" ? "none" : "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.toLocaleString(); populationDiv.appendChild(populationh2); header.append(populationDiv); var areaDiv = document.createElement("div"); var areah2 = document.createElement("h3"); areah2.innerText = area.toLocaleString(); 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.toLocaleString(); popdiv.appendChild(poph4); country.appendChild(popdiv); var areadiv = document.createElement("div"); var areah4 = document.createElement("h4"); areah4.innerText = area.toLocaleString(); areadiv.appendChild(areah4); country.appendChild(areadiv); return country; } // 绑定表头点击事件 document.getElementById('area').addEventListener('click', () => { updateSortState('area'); renderData(); }); document.getElementById('population').addEventListener('click', () => { updateSortState('population'); renderData(); }); document.getElementById('capital').addEventListener('click', () => { updateSortState('capital'); renderData(); }); document.getElementById('name').addEventListener('click', () => { updateSortState('name'); renderData(); }); // 初始化页面 init();
关键改动说明
- 缓存原始数据:页面加载时仅请求一次API,后续排序基于缓存的原始数据,提升性能并保留数据完整性
- 排序状态管理:用
sortStates数组记录每个字段的激活状态、排序方向,支持多列排序的优先级管理 - 组合排序比较器:动态生成排序逻辑,按激活的排序字段优先级依次比较,实现多列组合排序
- 修复数据异常:处理部分国家无首都、无面积的情况,避免排序报错
- 优化渲染逻辑:分离数据排序与渲染逻辑,每次排序后重新渲染页面,确保展示最新排序结果
- 数字格式化:为人口、面积添加千分位格式化,提升可读性
内容的提问来源于stack exchange,提问作者mat2254
相关产品推荐
相关产品推荐

