I am working on a project which moves project from on list to another. Here I have implemented a js for this projects 'Finish' and 'Activate' buttons which will switch them from 'Active Projects' to 'finished Projects' and vice versa.
class DOMHelper {
// Invoked using class
static clearEventListeners(element) {
// Deep copy the all the decendents of the element.
const clonedElement = element.cloneNode(true);
// Replacing the element with cloned Element
element.replaceWith(clonedElement);
// Return the clonedElement
return clonedElement;
}
// Invoked using class
static moveElement(eId, newDestSelector) {
// Get element Id of the source list
const element = document.getElementById(eId);
// Select the DOM of the destionation
const destElement = document.querySelector(newDestSelector);
// Appends the element to destination list
destElement.append(element);
}
}
class Tooltip {}
class ProjectItem {
constructor(id, updatePListFunc, type) {
this.id = id;
this.updatePListHandler = updatePListFunc;
this.connectSwitchBtn(type);
}
connectSwitchBtn(type) {
// Select the id of the list item
const pItemElement = document.getElementById(this.id);
// Selecting 'Finish' or 'Activate' button
let switchBtn = pItemElement.querySelector('button:last-of-type');
// Reset the event listeners after switching
switchBtn = DOMHelper.clearEventListeners(switchBtn);
// Set the type of the button according to the list type
switchBtn.textContent = type === 'active' ? 'Finish' : 'Activate';
// Adding eventListner
switchBtn.addEventListener('click', this.updatePListHandler.bind(null, this.id));
}
update(updatePListFunc, type) {
this.updatePListHandler = updatePListFunc;
this.connectSwitchBtn(type);
}
}
class ProjectList {
// Property
projects = []; // Empty list for the projects
// Active or Finished type
constructor(type) {
this.type = type;
const pItems = document.querySelectorAll(`#${type}-projects li`);
// Iterate all list items
for (const pItem of pItems) {
// Put them into the projects array
this.projects.push(
// Creates new obj
// with id of list item, a function, and its type
// Initializes and stores the project items for the list
new ProjectItem(pItem.id, this.switchProject, this.type)
);
}
console.log(this.projects);
}
// Sets switch handler function for the project List.
setSwitchHandlerFunc(switchHandlerFunc) {
// Refers to the current instance of ProjectList class
// Calling setSwitchHandlerFunc
this.switchHandler = switchHandlerFunc;
}
// Adds project to a List
addProject(project) {
// Push the new project into the array
this.projects.push(project);
// Moves the project form one list to another
DOMHelper.moveElement(project.id, `#${this.type}-project ul`);
// Update the project list
// Requires the type of the item and function
project.update(this.switchProject.bind(this), this.type);
}
// Switches projects from one list to another
switchProject(pId) {
// Select the list item to be removed
this.setSwitchHandlerFunc(this.projects.find(p => p.id === pId));
// Removes list item from one list
this.projects = this.projects.filter(p => p.id !== pId);
}
}
class App {
// Invoked using class.
static init() {
// creating instances for active and finished projects.
const activeProjectsList = new ProjectList('active')
const finishedProjectsList = new ProjectList('finished');
// Active project points to finished project
// When clicked on Finish
activeProjectsList.setSwitchHandlerFunc(
finishedProjectsList.addProject.bind(finishedProjectsList)
);
// Finished project points to active project
// When clicked on Activate
finishedProjectsList.setSwitchHandlerFunc(
activeProjectsList.addProject.bind(activeProjectsList)
);
}
}
// calls the static method.
App.init();
* {
box-sizing: border-box;
}
html {
font-family: sans-serif;
}
body {
margin: 0;
}
#main-header {
width: 100%;
height: 6rem;
display: flex;
justify-content: center;
align-items: center;
background: #ff0062;
}
#main-header h1 {
color: white;
margin: 0;
}
footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: 1rem 0;
}
section {
margin: 1rem auto;
width: 40rem;
max-width: 90%;
}
section ul {
padding: 1rem;
max-height: 20rem;
overflow: scroll;
}
section>h2 {
color: white;
margin: 0;
}
button {
font: inherit;
background: #ff0062;
color: white;
border: 1px solid #ff0062;
padding: 0.5rem 1.5rem;
cursor: pointer;
}
button.alt {
background: white;
color: #ff0062;
}
button:focus {
outline: none;
}
button:hover,
button:active {
background: #ff2579;
border-color: #ff2579;
color: white;
}
.card {
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
padding: 1rem;
background: white;
}
#active-projects {
border: 1px solid #870099;
}
#active-projects>header {
background: #870099;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#active-projects header h2 {
color: white;
margin: 0;
}
#finished-projects {
border: 1px solid #535353;
}
#finished-projects>header {
background: #535353;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#finished-projects header h2 {
color: white;
margin: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Project Board</title>
<link rel="stylesheet" href="assets/styles/app.css" />
<script src="assets/scripts/app1.js" defer></script>
</head>
<body>
<header id="main-header">
<h1>Project Planner</h1>
</header>
<section id="active-projects">
<header>
<h2>Active Projects</h2>
</header>
<ul>
<li id="p1" data-extra-info="Got lifetime access, but would be nice to finish it soon!" class="card">
<h2>Finish the Course</h2>
<p>Finish the course within the next two weeks.</p>
<button class="alt">More Info</button>
<button>Finish</button>
</li>
<li id="p2" data-extra-info="Not really a business topic but still important." class="card">
<h2>Buy Groceries</h2>
<p>Don't forget to pick up groceries today.</p>
<button class="alt">More Info</button>
<button>Finish</button>
</li>
</ul>
</section>
<section id="finished-projects">
<header>
<h2>Finished Projects</h2>
</header>
<ul>
<li id="p3" data-extra-info="Super important conference! Fictional but still!" class="card">
<h2>Book Hotel</h2>
<p>
Academind conference takes place in December, don't forget to book a hotel.
</p>
<button class="alt">More Info</button>
<button>Activate</button>
</li>
</ul>
</section>
</body>
</html>
I am getting this error.
app1.js:106 Uncaught TypeError: Cannot read properties of null (reading 'setSwitchHandlerFunc')
at switchProject (app1.js:106:14)
I am not able to understand why correct context of 'this' is getting lost in the loop?