如何在JS中渲染点击的项目对象内嵌套的Todo数组?
Todo列表应用:仅渲染当前项目Todo的解决方案
问题描述
我正在开发一个Todo列表应用作业,其中todos数组嵌套在projects对象中,代码按模块拆分:projectsArray存储在projects.js,DOM渲染代码存储在dom.js。
项目数据结构:
projectsArray = [ { title: 'Client Presentation', todos: [ { title: 'Project Proposal', date: '2023-11-15', important: true, completed: false }, { title: 'Presentation Draft', date: '2023-11-21', important: false, completed: false } ] }, { title: 'Shareholders Meeting', todos: [ { title: 'Quarterly Reports', date: '2023-11-18', important: false, completed: false }, { title: 'Projected Future Earnings', date: '2023-11-22', important: false, completed: false } ] } ];
侧边栏渲染项目列表的代码:
// RENDER PROJECTS IN SIDEBAR function renderProjectsSidebar() { sidebarProjects.innerText = ''; projects.projectsArray.forEach((project, index) => { // Create list item for each project const projectItems = createHtmlElement('li', null, ['header-project-items', '|', 'no-bullets', 'flex', 'gap-sm', 'align-items-center'], 'data-index', index, null, headerProjects); const projectTitle = createHtmlElement('p', null, ['header-project-title'], null, null, project.title, projectItems); // EVENT: Render project to page projectItems.addEventListener('click', () => { renderProjectsPage(project, index); }) }); };
当前renderProjectsPage代码可渲染项目标题及操作按钮,但无法仅展示点击项目对应的Todo数组,点击项目时会渲染所有项目的Todo:
// RENDER PROJECTS TO PAGE function renderProjectsPage(project, index) { const mainContainer = document.querySelector('#mainContainer'); mainContainer.setAttribute('data-index', index); clearMainContainer() const todoHeaderContainer = createHtmlElement('div', null, ['todo-header-container'], null, null, null, mainContainer); const todoHeaderTitle = createHtmlElement('h2', null, ['todo-header__title', '|', 'title--sm', 'flex', 'justify-content-between'], null, null, project.title, todoHeaderContainer); const todoHeaderBtns = createHtmlElement('div', null, ['todo-header__btn-container', 'flex', 'gap-sm', 'align-items-center'], null, null, null, todoHeaderContainer); // Create add todo button const addTodoBtn = createHtmlElement('i', 'addTodoBtn', ['btn--add', '|', 'fa-solid', 'fa-circle-plus'], 'data-action', 'edit', null, todoHeaderBtns); addTodoBtn.addEventListener('click', () => todoModal.showModal()); // Create edit button for project const projEditBtn = createHtmlElement('i', null, ['btn--edit', '|', 'fas', 'fa-edit'], 'data-action', 'edit', null, todoHeaderBtns); // Create delete button for project const projDeleteBtn = createHtmlElement('i', null, ['btn--delete', '|', 'fa-solid', 'fa-trash'], 'data-action', 'delete', null, todoHeaderBtns); // EVENT: Delete project projDeleteBtn.addEventListener('click', () => projects.deleteProject()) // Create container for todos const todoContainer = createHtmlElement('div', 'todoContainer', ['todo-container'], null, null, null, mainContainer) renderTodos(); };
尝试过嵌套forEach和结合filter的方式,但逻辑错误,未能实现目标:
function renderTodos() { const currentProject = document.querySelector('#mainContainer').getAttribute('data-index'); const todoContainer = document.querySelector('#todoContainer'); const filteredTodos = projects.projectsArray.filter((currentProject) => { currentProject.todos.forEach((todos, index) => { const todoItems = createHtmlElement('li', null, ['todo-item'], 'data-index', index, null, todoContainer); const todoTitle = createHtmlElement('h3', null, ['todo-item__title'], 'data-action', 'check', todos.title, todoContainer); }) })
需要实现点击项目时仅渲染该项目对应的Todo数组。
解决方案
核心问题是未正确定位当前项目的todos数组,以下两种方式可快速解决:
方式一:直接传递当前项目对象(最优解)
既然renderProjectsPage已经拿到了点击的project对象,直接将其传给renderTodos,无需从DOM读取索引:
- 修改
renderProjectsPage中的调用:
// 替换原有的renderTodos(); renderTodos(project);
- 重写
renderTodos函数:
function renderTodos(project) { const todoContainer = document.querySelector('#todoContainer'); // 渲染前清空容器,避免旧内容残留 todoContainer.innerText = ''; // 直接遍历当前项目的todos数组 project.todos.forEach((todo, index) => { const todoItems = createHtmlElement('li', null, ['todo-item'], 'data-index', index, null, todoContainer); // 将标题添加到todoItems中,保证DOM结构正确 const todoTitle = createHtmlElement('h3', null, ['todo-item__title'], 'data-action', 'check', todo.title, todoItems); }); }
方式二:通过索引获取当前项目
如果需要保留从DOM读取索引的逻辑,需修正filter的用法——filter用于筛选数组元素,你之前的写法逻辑错误,应先通过索引定位项目,再遍历其todos:
function renderTodos() { // 将字符串索引转为数字 const currentProjectIndex = parseInt(document.querySelector('#mainContainer').getAttribute('data-index')); const todoContainer = document.querySelector('#todoContainer'); todoContainer.innerText = ''; // 通过索引直接获取当前项目,加判断避免索引无效 const currentProject = projects.projectsArray[currentProjectIndex]; if (!currentProject) return; // 遍历当前项目的todos currentProject.todos.forEach((todo, index) => { const todoItems = createHtmlElement('li', null, ['todo-item'], 'data-index', index, null, todoContainer); const todoTitle = createHtmlElement('h3', null, ['todo-item__title'], 'data-action', 'check', todo.title, todoItems); }); }
额外注意事项
- 渲染前必须清空
todoContainer,防止旧Todo重复显示 - 确保将
todoTitle添加到todoItems中,而非直接添加到todoContainer,否则DOM结构会混乱 projects.deleteProject()方法需传入当前项目索引,才能正确删除目标项目
内容的提问来源于stack exchange,提问作者Ryan Hayes
相关产品推荐
相关产品推荐

