You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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读取索引:

  1. 修改renderProjectsPage中的调用:
// 替换原有的renderTodos();
renderTodos(project);
  1. 重写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 01:54:55