I'm a Kotlin engineer looking to get into web dev. I thought it'd be fun to make a Todo app w/o help from the internet. Just looking for some critiques and feedback, as I'm new-ish to the language.
Here's the JS, HTML, CSS. Looking for criticism on all of it. No mobile support btw.
JS:
class Column {
constructor(elementId, countId) {
this.items = new Map();
this.count = this.items.size;
this.elementId = elementId;
this.countId = countId;
this.node = document.getElementById(this.elementId);
this.countNode = document.getElementById(this.countId);
}
updateCount() {
this.count = this.items.size;
this.countNode.innerHTML = "|(" + this.count.toString() + ")";
}
}
class Todo extends Column {
constructor() {
super("todo", "todoCount");
}
add(item) {
item.toolbar.appendChild(item.close);
if (item.complete) {
item.checkbox.addEventListener("click", () => {
closeItem(item);
});
item.complete = false;
}
this.items.set(item, item.node);
this.node.appendChild(item.node);
this.updateCount()
}
remove(item) {
this.items.delete(item);
item.node.remove();
item.complete = true;
this.updateCount();
}
}
class Done extends Column {
constructor() {
super("done", "doneCount");
}
add(item) {
item.checkbox.addEventListener("click", () => {
openItem(item);
});
this.items.set(item, item.node);
this.node.appendChild(item.node);
this.updateCount();
}
remove(item) {
this.items.delete(item);
item.node.remove();
this.updateCount();
}
}
class TodoItem {
constructor() {
this.node = null;
this.toolbar = null;
this.checkbox = null;
this.close = null;
this.complete = false;
}
generateName() {
return Math.floor(Math.random() * 1000).toString();
}
create() {
// Create the parent of the todo item
let item = document.createElement("article");
item.id = this.generateName();
// Create a checkbox, and its parent
let todoToolbar = document.createElement("span");
todoToolbar.className = "todoToolbar";
let checkbox = document.createElement("input");
checkbox.className = "checkbox";
checkbox.type = "checkbox";
let close = document.createElement("span");
close.className = "close"
close.textContent = "✖";
// Create the Todo input area
let input = document.createElement("textarea");
input.className = "todoInput";
checkbox.addEventListener("click", () => {
closeItem(this);
});
close.addEventListener("click", () => {
deleteItem(this);
});
// Assign children and set the node
todoToolbar.appendChild(checkbox);
todoToolbar.appendChild(close);
item.appendChild(todoToolbar);
item.appendChild(input);
this.node = item;
this.toolbar = todoToolbar;
this.checkbox = checkbox;
this.close = close;
}
}
let todo = new Todo();
let done = new Done();
function closeItem(item) {
todo.remove(item);
done.add(item);
}
function openItem(item) {
done.remove(item);
todo.add(item);
}
function deleteItem(item) {
if (done.items.has(item)) {
done.items.delete(item);
done.updateCount();
}
if (todo.items.has(item)) {
todo.items.delete(item);
todo.updateCount();
}
item.node.remove();
}
function createItem() {
let item = new TodoItem();
item.create();
todo.add(item);
}
function main() {
document.getElementById("addTodo").onclick = createItem;
todo.updateCount();
done.updateCount();
}
main();
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="index.css">
<title>Title</title>
</head>
<body>
<main id="todoParent">
<article id="functionality">
<section id="toolbar">
<button id="addTodo">Add Todo</button>
</section>
<section id="columns">
<section id="todo">
<div class="columnToolbar">
<h1>Todo</h1>
<h1 id="todoCount"></h1>
</div>
</section>
<section id="done">
<div class="columnToolbar">
<h1>Done</h1>
<h1 id="doneCount"></h1>
</div>
</section>
</section>
</article>
</main>
<script src="index.js"></script>
</body>
</html>
CSS:
html {
min-height: 100%;
position: relative;
}
body {
height: 100%;
}
button {
z-index: 10;
}
#todoParent {
display: flex;
justify-content: center;
height: 100%;
top:0;
bottom:0;
left:0;
right:0;
overflow:hidden;
background-color: darkslategray;
position:absolute;
}
#functionality {
width: 40%;
background-color: white;
}
#columns {
display: flex;
flex-direction: row;
justify-content: center;
}
#columns h1 {
text-align: center;
}
#columns section {
background-color: #e0e0e0;
border: black solid 1px;
width: 250px;
height: 90vh;
margin: 5%;
overflow-y: scroll;
}
#columns article {
display: flex;
flex-direction: column;
justify-content: flex-start;
background-color: white;
width: 200px;
height: 200px;
margin: 5%;
padding: 5%;
}
#toolbar {
display: flex;
justify-content: center;
flex-direction: row;
background-color: #e0e0e0;
}
#toolbar button {
margin: 5px;
}
#todoCount {
margin-left: 1%;
}
.todoToolbar {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.todoCheckbox {
margin: 5%;
}
.todoInput {
height: 100%;
box-sizing: border-box;
resize: vertical;
}
.columnToolbar {
display: flex;
flex-direction: row;
justify-content: center;
}
```