I'm currently working on a To-do-list from the odin project. As per the instructions, my todolist can create a new project, which we can then add tasks to the project. I now have a sidebar listing all existing projects, and when I click on any of the projects, it renders the details of the project onto the main body. I've attached screenshots of the page
before I click on a project
after I click on a project
I'm currently having trouble trying to add new tasks to the project that's active on the main body as I can't get its index.
In my addTaskModal.js module, I've created a dialog popup for users to input the details of the tasks to be added to the active project. But I've been stuck for hours and I can't figure out how to get the index of the project that's rendered on the main page.
Here's my code that renders the project to the body:
import {
addTaskModal
} from './addTaskModal.js';
import {
myProjects
} from './myProjects.js';
const mainContent = document.querySelector('#main');
export default function renderMain(event) { //render project from sidebar to mainContent body
mainContent.textContent = '';
mainContent.appendChild(addTaskModal); //dialog to input new task and its priority
let index = parseInt(event.target.id.split('-')[1]);
const projectCard = document.createElement('div');
projectCard.classList.add('card');
const projectCardHeader = document.createElement('h3');
projectCardHeader.classList.add('cardheader');
projectCardHeader.textContent = myProjects[index].name
projectCard.appendChild(projectCardHeader);
const projectDue = document.createElement('div');
projectDue.textContent = myProjects[index].due;
projectDue.classList.add('date');
projectCard.appendChild(projectDue);
for (let i = 0; i < myProjects[index].tasks?.length; i++) {
const tasks = document.createElement('div');
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete Task';
deleteBtn.addEventListener('click', () => {
myProjects[index].tasks.splice(i, 1);
localStorage.setItem('projects', JSON.stringify(myProjects));
renderMain(event);
})
//when we want to update after delete, just store with same "key" and the "key" will be updated.
tasks.textContent = myProjects[index].tasks[i].name;
tasks.classList.add('tasks');
tasks.appendChild(deleteBtn);
projectCard.appendChild(tasks);
}
const addTaskBtn = document.createElement('button');
addTaskBtn.textContent = "Add Task";
mainContent.appendChild(addTaskBtn);
addTaskBtn.addEventListener('click', () => {
addTaskModal.showModal();
});
mainContent.appendChild(addTaskBtn);
mainContent.appendChild(projectCard)
}
Here's the code for myProjects array for context:
let myProjects = JSON.parse(localStorage.getItem('projects')) || [{
name: "Go to supermarket",
due: "12/2/25",
tasks: [{
name: "Buy apples",
priority: "high"
},
{
name: "Buy Eggs",
priority: "medium"
}
]
},
{
name: "Clean the house",
due: "13/2/25",
tasks: [{
name: "Sweep and mop the floor",
priority: "high"
},
{
name: "Do laundry",
priority: "high"
}
]
}
];
export {
myProjects
};
Here's the addTaskModal.js to create a popup dialog for users to input details of the new task
import { myProjects } from "./myProjects";
const addTaskModal = document.createElement('dialog');
addTaskModal.classList.add('modal');
//const addTaskModal = document.createElement('div');
//addTaskModal.classList.add('hidden');
//create label and input box for user to input new task title
const newTask = document.createElement('div');
newTask.classList.add('modallabel');
newTask.textContent = "New Task :";
const newTaskInput = document.createElement('input');
newTaskInput.setAttribute('placeholder', 'Add New Task Here');
newTaskInput.setAttribute('id', 'newtaskinput');
newTaskInput.classList.add('inputbox');
newTask.appendChild(newTaskInput);
//create label and select input for user to input new task priority
const newPriority = document.createElement('div');
newPriority.textContent = "Priority: ";
newPriority.classList.add('prioritylabel');
const newPriorityInput = document.createElement('select');
newPriorityInput.setAttribute('id', 'newpriorityinput');
//add options to select
const selectHigh = document.createElement('option');
selectHigh.textContent = "High";
selectHigh.setAttribute('value', 'High')
newPriorityInput.appendChild(selectHigh);
const selectMedium = document.createElement('option');
selectMedium.textContent = "Medium";
newPriorityInput.appendChild(selectMedium);
const selectLow = document.createElement('option');
selectLow.textContent = "Low";
newPriorityInput.appendChild(selectLow);
newPriority.appendChild(newPriorityInput);
//button to submit new task to the project
const submitNewTaskBtn = document.createElement('button');
submitNewTaskBtn.textContent = "Add Task";
addTaskModal.appendChild(newTask);
addTaskModal.appendChild(newPriority);
addTaskModal.appendChild(submitNewTaskBtn);
export { addTaskModal };
export { submitNewTaskBtn };
Here's the main index.js where I have the renderSidebar() function to populate the sidebar with the projects list:
import './styles.css';
import { myProjects } from './myProjects.js';
import renderMain from './renderMain.js';
import createTask from './addTask.js';
import { addTaskModal } from './addTaskModal.js';
const mainContent = document.querySelector('#main');
const sideBar = document.querySelector('#sidebarproject');
function renderSidebar() {
let sidebarHTML = '';
myProjects.forEach((project, index) => {
sidebarHTML += `<div class="projects" id="project-${index}">${project.name}</div>`
})
sideBar.innerHTML += sidebarHTML;
const projects = document.querySelectorAll('.projects');
projects.forEach(project =>
project.addEventListener('click', renderMain)
)
}
renderSidebar();
mainContent.appendChild(addTaskModal);
I notice
renderMain(event)
and
let index = parseInt(event.target.id.split('-')[1]);
This is not going to work if the event you are sending to the rendermain does not have an ID, like your delete button.
I suggest you move
localStorage.setItem('projects', JSON.stringify(myProjects));
to the rendermain and change
function renderMain(event) {
let index = parseInt(event.target.id.split('-')[1]);
to
function renderMain(index,projectName)
I ALSO suggest you delegate the click to the container, then you can do
deleteBtn.classList.add('delete');
deleteBtn.textContent = 'Delete Task';
deleteBtn.dataset.index = i;
deleteBtn.dataset.projectname = projectName;
and change
deleteBtn.addEventListener('click', () => {
myProjects[index].tasks.splice(i, 1);
localStorage.setItem('projects', JSON.stringify(myProjects));
renderMain(event)
})
to
mainContent.addEventListener('click', (e) => {
const tgt = e.target.closest('button.delete');
if (!tgt) return;
myProjects[tgt.dataset.index].tasks.splice(tgt.dataset.index, 1);
renderMain(tgt.dataset.index,tgt.dataset.projectname);
});
outside the function
Here is a simpler version of your render
const renderSidebar => () {
sideBar.innerHTML = myProjects
.map((project, index) => `<div class="projects" data-name="${project.name}" data-index="${index}">${project.name}</div>`).join('');
};
sideBar.addEventListener('click', (e) => {
const tgt = e.target.closest('div.projects');
if (!tgt) return; // we did not click in a project
renderMain(tgt.dataset.index, tgt.dataset.name)
})
In your modal, you need to capture the index
Here is the changes to addModal
submitNewTaskBtn.addEventListener('click', () => {
myProjects.tasks.push(newTask);
renderMain(myProjects.length-1,newTaskInput.value);
})