如何用JS处理嵌套对象数组实现层级选择菜单功能?
层级分类选择菜单实现步骤
一、搭建基础HTML结构
先写出核心DOM结构,包含触发按钮、弹出层、已选列表:
<button id="openMenuBtn">选择分类</button> <!-- 弹出层 --> <div id="categoryModal" class="modal hidden"> <div class="modal-header"> <button id="backBtn" class="hidden">← 返回上一级</button> <span>选择分类</span> </div> <div id="categoryList" class="category-list"></div> </div> <!-- 已选列表 --> <div class="selected-container"> <h3>已选分类</h3> <ul id="selectedList"></ul> </div>
二、JavaScript核心逻辑实现
1. 定义状态变量
声明需要跟踪的核心状态,放在全局或模块作用域:
const menu = [{ categories: [{ id: 1, name: 'Desserts', categories: [{ id: 2, name: 'Ice cream', categories: [{ id: 3, name: 'Vanilla', categories: [] }, { id: 4, name: 'Strawberry', categories: [] }] }, { id: 5, name: 'Baked apples', categories: [] }] }, { id: 6, name: 'Coffee', categories: [] }, { id: 7, name: 'Tea', categories: [{ id: 8, name: 'Green tea', categories: [{ id: 9, name: 'With Jasmine', categories: [] }, { id: 10, name: 'Simple', categories: [] }] }, { id: 11, name: 'Black Tea', categories: [] }] }] }]; // 状态变量 let currentLevel = menu[0].categories; // 当前展示的层级数据 let levelHistory = []; // 层级历史,用于返回上一级 let selectedCategories = []; // 已选分类列表 // DOM元素引用 const openBtn = document.getElementById('openMenuBtn'); const modal = document.getElementById('categoryModal'); const backBtn = document.getElementById('backBtn'); const categoryList = document.getElementById('categoryList'); const selectedList = document.getElementById('selectedList');
2. 绑定基础交互事件
给触发按钮、返回按钮绑定点击逻辑:
// 打开弹出层 openBtn.addEventListener('click', () => { modal.classList.remove('hidden'); renderCategoryList(currentLevel); updateBackBtnStatus(); }); // 返回上一级 backBtn.addEventListener('click', () => { if (levelHistory.length === 0) return; currentLevel = levelHistory.pop(); renderCategoryList(currentLevel); updateBackBtnStatus(); });
3. 渲染当前层级分类选项
编写函数根据当前层级数据生成可点击的选项:
function renderCategoryList(data) { categoryList.innerHTML = ''; // 清空当前列表 data.forEach(item => { const li = document.createElement('li'); li.className = 'category-item'; li.textContent = item.name; // 绑定点击事件 li.addEventListener('click', () => { if (item.categories.length > 0) { // 有子分类,进入下一级 levelHistory.push(currentLevel); currentLevel = item.categories; renderCategoryList(currentLevel); updateBackBtnStatus(); } else { // 无子分类,加入已选列表 selectedCategories.push({ id: item.id, name: item.name }); renderSelectedList(); modal.classList.add('hidden'); // 可选:选完关闭弹出层 } }); categoryList.appendChild(li); }); }
4. 控制返回按钮显示状态
根据层级历史判断是否显示返回按钮:
function updateBackBtnStatus() { backBtn.classList.toggle('hidden', levelHistory.length === 0); }
5. 渲染已选列表(支持删除)
编写函数生成已选列表,并实现删除功能:
function renderSelectedList() { selectedList.innerHTML = ''; if (selectedCategories.length === 0) { const emptyLi = document.createElement('li'); emptyLi.textContent = '暂无选择'; selectedList.appendChild(emptyLi); return; } selectedCategories.forEach((item, index) => { const li = document.createElement('li'); li.innerHTML = `${item.name} <button class="del-btn" data-index="${index}">删除</button>`; // 删除按钮事件 li.querySelector('.del-btn').addEventListener('click', (e) => { const idx = e.target.dataset.index; selectedCategories.splice(idx, 1); renderSelectedList(); }); selectedList.appendChild(li); }); }
三、关键CSS样式参考
基于你的HTML/CSS能力,给出核心样式方向,细节可自行调整:
/* 弹出层基础样式 */ .modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); border: 1px solid #ccc; padding: 16px; background: white; z-index: 1000; } /* 隐藏类 */ .hidden { display: none; } /* 分类列表样式 */ .category-list { list-style: none; padding: 0; margin: 16px 0; } .category-item { padding: 8px; cursor: pointer; border-radius: 4px; } .category-item:hover { background-color: #f5f5f5; } /* 已选列表样式 */ .selected-container { margin-top: 20px; } .del-btn { margin-left: 8px; padding: 2px 6px; cursor: pointer; }
四、测试与边界处理
- 验证返回按钮:处于最顶层时,返回按钮自动隐藏
- 验证末级分类:点击后正确加入已选列表,弹出层正常关闭
- 验证删除功能:删除已选项后列表实时更新
- 可选优化:若需支持选择中间层级(如直接选「Desserts」而非末级),可给每个选项添加「选择当前分类」的独立按钮,或修改点击逻辑增加选择判断
内容的提问来源于stack exchange,提问作者Toma
相关产品推荐
相关产品推荐

