<svg style="display:none;">
  <symbol id="input" viewBox="0 0 345.27 56.51" preserveAspectRatio="none">
    <path fill="none" stroke="#000" stroke-miterlimit="10" d="M5.51 3.68c10.87 0 21.78.34 32.64.05 4.83-.13 9.56-1.01 14.4-1.09 4.36-.07 8.7.32 13.05-.18C73.31 1.57 81 1.99 88.68.9 94.06.14 99.8.67 105.22.68c4.66 0 9.52-.46 14.14.22 2.7.4 4.94.83 7.68.8 6.99-.09 13.98-.02 20.97-.02 14.77 0 29.36.72 44.09.96 4.19.07 8.23.88 12.39 1.09 5.06.25 10.22-.05 15.28-.05H328.89c4.85 0 10.8-.95 15.52.15-1.79 8.51-.89 17.99-.89 26.86 0 4.3-.79 9.52.22 13.77.53 2.22 2.1 7.95-.09 9.29-.72.44-3.37-.07-4.28-.07-3.14-.01-6.27 0-9.41 0-5.06 0-9.92-.1-14.96-.54-5.99-.52-11.8.61-17.8.56-5.9-.05-11.89-.27-17.79-.07-6.15.2-12.19 1.11-18.36 1.07-7.39-.05-14.51.03-21.85.75-16.65 1.64-33.48-.94-50.14-.75-5.79.06-11.47-1.07-17.31-1.07-5.88 0-11.66.94-17.52 1.08-13.9.35-27.86-.04-41.76-.04-12.19 0-24.2-.99-36.37-1-12.84-.01-25.54-1-38.37-1-3.57 0-7.15.03-10.72.02-2.78 0-5.28-.88-8.02-1.07-4.63-.32-9.36.23-13.99.07-2.53-.08-2.06.11-2.49-1.99-.42-2.02-.95-3.87-1.03-6-.1-2.67.33-5.4-.06-8.05-.53-3.64-.91-6.52-.91-10.28V9.67c0-1.66.04-3.33 0-5-.06-2.93-.01-3.06 3-3 1.92.04 3.88-.14 5.8-.03 1.89.11 2.7.95 4.61 1.03 2.52.11 5.06-.05 7.58-.01 2.04.03 4.15-1.39 6.01-1" />
  </symbol>
  <symbol id="checkbox_empty" viewBox="0 0 33.18 33.34">
    <path fill="none" stroke="#000" stroke-miterlimit="10" d="M31.74 0v18c0 2.74 1.29 12.29-.5 13.96-1.65 1.53-7.08-.9-8.91-1-2.68-.15-5.44.04-8.13.04-2.41 0-12.1-1.27-13.53.63C1.16 26.5.74 21.15.74 16c0-2.65-.17-5.36-.02-8 .08-1.5.58-2.6 1.05-4.02.67-2.02-.31-1.91 2.46-1.99C9.59 1.85 14.97 2 20.32 2c2.97 0 5.95-.08 8.92-.01 1.95.05 1.97-.23 2.53 1.47.44 1.34.28 2.19.96 3.54" />
  </symbol>
  <symbol id="checkmark" viewBox="0 0 37.92 33.3" preserveAspectRatio="none">
    <path fill="none" stroke="#000" stroke-miterlimit="10" d="M.59 19.57c1.41.11 2.74 1.49 3.93 2.22 1.96 1.22 4.34 1.97 6.16 3.29 1.94 1.41 3.17 3.68 5.03 5.09-1.01-1.05-2.52-1.47-3.7-2.3-2.15-1.5-4.06-2.99-6.49-4.1-1.46-.67-6.06-1.54-4.8-4.07 1.76-.33 5.43 3.09 6.68 4.15 1.47 1.24 2.42 2.87 3.98 4 2.01 1.45 2.09 1.24 3.97-.52 1.43-1.34 2.28-2.68 3.24-4.25 1.04-1.71 2.62-3.12 3.79-4.72.31-.42.64-1.52.93-2 .87-1.48 2.1-2.58 3.3-3.77 2.23-2.22 4.27-4.42 6.27-6.8.59-.7 1.46-1.2 2-1.93.58-.77.73-1.66 1.33-2.42C33 3 31.46 7.7 29.34 10.35c-1.99 2.48-4.31 4.87-5.75 7.71-2.38 4.67-5.6 10.27-9.65 13.43.83-1.6 2.49-2.55 3.65-3.87 1.51-1.72 2.8-3.72 4.22-5.52 1.87-2.37 3.39-4.85 4.96-7.23 1.27-1.94 3.29-4.64 5.02-6.3 1.25-1.19 2.15-1.5 3.07-3 .61-.99.87-1.86 1.83-2.6-4.07 4.06-7.78 8.84-11.07 13.55-2.11 3.02-4.17 6.06-6.3 9.07-1 1.42-3.02 5.22-4.37 5.89.89-1.76 2.86-2.94 4.07-4.49 2.66-3.42 4.5-7.43 7.04-10.94 1.59-1.98 3.18-3.97 4.77-5.96.41-.84.74-1.72 1-2.62.72-1.07 1.95-1.79 2.76-2.91" />
  </symbol>
  <symbol id="button" viewBox="0 0 256.6 60.02" preserveAspectRatio="none">
    <path d="M3.98 0c9.31-.01 18.44.99 27.76 1 10.44.01 20.89 0 31.33 0 7.99 0 16.09-.41 24.06.22 10.76.85 21.39.78 32.2.78 4.39 0 8.82.21 13.2.05 4.46-.16 8.62-1.07 13.12-1.05 19.4.1 38.81 0 58.21 0 4.58 0 9.16.02 13.74-.01 5.99-.04 11.84.86 17.8 1.06 6.92.24 14.3.49 21.2-.12-.87.98-1.21 2.86-1.65 4.09-.31.89-.81 2-1.02 2.93-.78 3.31.16 7.86.07 11.25-.1 3.99-1.02 7.75-1.02 11.8v11.02c0 3.58.19 7.22.02 10.8-.13 2.66-.6 2.22-3.15 2.18-4.54-.08-9.09 0-13.63 0-5.38 0-10.75-.02-16.13.02-5.74.05-11.37-.86-17.09-1.07-6.42-.24-12.91.09-19.34.06-4.9-.03-9.57.9-14.44 1.05-2.54.08-5.26-.44-7.78.03-1.32.24-2.48.79-3.82.94-3.63.41-7.47.87-11.13 1.02-4.97.2-9.47-.21-14.38.73-6.11 1.17-13 .22-19.24.22-8.9 0-17.92-.4-26.81-.05-5.32.21-10.52 1.12-15.85 1.07-9.41-.07-18.81-.02-28.2-.02H12.98c-1.5 0-3-.02-4.5 0-1.59.02-2.54-.51-3.72-.78-.89-.2-4.02.24-4.65-.38-.48-.47.81-5.66.87-6.65.17-2.71 1.06-5.14 1.02-7.92-.04-3.34 0-6.68-.02-10.02-.02-4.3.96-7.94 1-12.27.03-4.3-1.41-8.11-2.05-12.2C.61 7.77.02.47 2.71.18c.77 1.2 2.03.84 3.27.82" />
  </symbol>
  <symbol id="close" viewBox="0 0 29.71 30.59">
    <path fill="none" stroke="#000" stroke-miterlimit="10" stroke-width=".936" d="M29.43.38c-2.74 1.96-4.3 5.53-7.23 7.73-.84.63-2.17 1.1-2.88 1.83-.65.66-.65 1.79-1.3 2.45-1.26 1.27-3.14 2.1-4.49 3.46-4.47 4.5-10.32 8.92-13.1 14.53" />
    <path fill="none" stroke="#000" stroke-miterlimit="10" d="M.35 2.73c9.03 8.88 18.06 17.77 27.08 26.65" />
  </symbol>
  <symbol id="stats" viewBox="0 0 998.06 602.62" preserveAspectRatio="none">
    <path fill="#000" stroke="#000" stroke-miterlimit="10" d="M994.5 591.62c-1.92-.28-2.94.46-4.7.78-1.94.35-3.79.23-5.75.22-5.48-.03-11.04.35-16.49-.22-15.32-1.59-30.5.22-45.87.22-17.9 0-35.67.99-53.55 1.02-23.01.03-45.98-1.08-69-.99-10.65.04-20.84-1.45-31.41-.25-5.25.6-10.55.06-15.81.19-4.71.11-9.32.88-14.05.98-5.57.12-10.92 1.38-16.56 1.11-3.03-.15-5.9-.99-8.94-1.1-6.01-.22-12.1.05-18.11.05-8.71 0-17.42.03-26.13 0-8.81-.02-17.47.82-26.25 1.05-4.06.11-8.13-.27-12.18-.1-3.25.13-6.41.84-9.66 1.05-12.86.82-25.59 1-38.48 1-15.24 0-30.48-.07-45.72.02-7.33.05-14.47-1.09-21.82-1.05-9.14.05-18.29-.03-27.43.02-7.09.04-14.01-1.09-21.1-1.02-11.56.11-23.13-.74-34.69-1.22-10.38-.43-20.68-.75-31.07-.75-10.62 0-21.09 1.09-31.71 1.03-12.91-.07-25.7 1.05-38.6 1-5.76-.02-11.58.22-17.34.03-4.19-.14-8.25-.97-12.43-1.1-17.17-.53-34.45.12-51.64.06-7.99-.03-15.84 1.08-23.82 1.02-6.77-.05-13.55-.02-20.32-.02-18.68 0-37.4.33-56.09.05-9.8-.14-19.51-1.05-29.34-1.05-12.69 0-25.29-.36-37.95-.97-2.54-.12-4.91-.92-7.43-1.08-3.74-.24-7.6.05-11.35.05h-34.6c-1.8 0-3.91-.27-5.69-.02-1.43.2-2.5.78-3.91 1.07-2.5.52-5.29-.41-7.8.17-.66.15-.94 1.58-2.03.68-.79-.67-.16-3.71-.17-4.64 0 1.55-.57 3.71 1.27 4.4 1.88-1.8.11-3.06-.35-4.75-.52-1.89.53-4.33.83-6.14.25-1.5.19-3.1.49-4.61 1.48-7.19 2.76-14.1 2.75-21.54-.01-5.2 0-10.4 0-15.61 0-1.96-.02-3.92 0-5.87.02-3.33-.67-5.94-1.02-9.07-.29-2.6-.11-4.59-.75-7.06-.46-1.74-.22-3.62-.2-5.42.02-2.04-.61-3.49-.82-5.38-.21-1.81.22-2.18.58-3.98.72-3.58.22-7.63.22-11.25 0-7.17-1-14.02-1-21.15 0-10.51.45-21.14.02-31.64-.11-2.8-.91-5.44-1.05-8.22-.14-2.81 0-5.7.02-8.51.02-2.97-.87-5.14-1.05-8.02-.48-7.89.05-15.99.05-23.9 0-12.72.39-25.45-.78-38.12-.5-5.43-.25-10.96-.23-16.41.03-7.9-1.07-15.61-.97-23.51.09-6.88-1.19-13.66-1.05-20.57.11-5.62-.59-11.39-1.22-16.97-.54-4.8-.41-9.45-.7-14.25-.13-2.23-.98-4.2-1.1-6.43-.15-2.56.13-5.16.1-7.73C1.46 237.09.51 227.08.5 217c-.01-8.65.03-17.3 0-25.94-.02-6.79 1.07-13.39 1.02-20.14-.07-8.48 0-16.96-.02-25.44-.02-8.68.54-17.37 1.77-25.96 1.14-7.94.07-16.46.15-24.4.17-15.27 5-29.97 5.08-45.26.03-6.14-.94-12.12-1.02-18.18-.03-2.35.21-4.75.07-7.1-.2-3.38-1.42-6.71-1.85-10.08-.66-5.19-.2-5.66-.2-10.88h16c2.33 0 4.66-.03 6.99-.02 4.21.01 8.3 1.02 12.51 1.12 3.94.1 7.81-1.07 11.74-1.14 2.77-.05 5.55.04 8.32.04 5.03 0 10.06.02 15.08 0 8.75-.03 17.36 1.02 26.12.99 5.28-.02 10.57-.01 15.85-.03 8.14-.04 16.19 1.12 24.33 1.07 15.14-.08 30.5.72 45.6-.12 5.1-.29 10.04-.97 15.17-.94 4.18.02 8.39.21 12.57.07 4.45-.15 8.8-1.01 13.26-1.09 4.5-.08 9.01.04 13.5.04h65.38c12.02 0 24.03-.06 36.05.01 3.86.02 7.3-.3 11.12-.79 5.67-.72 11.6-.22 17.3-.22 9.03 0 18.07.01 27.1-.04 5.81-.03 11.52.89 17.31 1.09 5.04.18 10.11-.29 15.15-.11 3.59.13 7.29 1.36 10.89.88 3.69-.49 6.96-.86 10.72-.84 9.14.05 18.07-.04 27.16.79 12.28 1.11 24.95.26 37.28.19 4.69-.03 9.24.95 13.91 1.09 3.75.11 7.54-.05 11.29-.05 3.88 0 7.83.31 11.69-.09 3.77-.38 7.14-.96 11-.94 10.26.07 20.61-.43 30.85.25 14.62.97 29.2.78 43.85.78 8.67 0 17.59.61 26.24.05 2.93-.19 5.63-1.03 8.6-1.08 3.56-.06 6.67-.19 10.16-.75 2.84-.45 5.72-.23 8.59-.21 3.81.02 6.96-.38 10.65-.93 4.46-.66 9.38-.12 13.9-.09 7.32.05 14.58-1 21.97-.99 15.43.02 30.86 0 46.3 0 18.36 0 37.12-1.09 55.33.92 3.74.41 7.18-.28 10.87-.7 4.22-.48 8.52-.23 12.75-.22 4.61.01 9.21-.01 13.82.02 5.5.04 10.86-.94 16.35-1.07 7.23-.18 14.5.05 21.73.05 3.72 0 7.44.01 11.17-.02 4.34-.04 8.43.93 12.75 1.07 4.74.16 9.53-.05 14.28-.05 2.76 0 5.52.01 8.28-.02 2.98-.03 5.63.94 8.59 1.07 5.02.23 10.11-.06 15.13-.05 3.57 0 5.11-.67 6.71 2.78.9 1.93.74 3.93.73 6.22-.02 2.55.93 4.57 1.03 7 .1 2.5 1 4.49.97 7-.04 2.67 0 5.35 0 8.03 0 5.15.12 10.32.01 15.47-.05 2.32-1.04 4.14-1.01 6.5.03 2.5 0 4.99 0 7.49 0 12.65.22 25.32.01 37.97-.05 3-.78 6.2-1.51 9.05-.59 2.3-.53 4.56-.5 6.99.03 2.99-.96 5.38-1 8.39-.07 6.2-1 12.35-1 18.62 0 4.01-.01 8.02 0 12.02.02 7.64-1.02 15.13-.99 22.76.02 4.36.53 9-.5 13.22-.4 1.65-.51 2.92-.47 4.59.09 4.04-.99 7.92-1.08 11.95-.15 6.27-.97 12.36-.94 18.63.02 4.53 0 9.05 0 13.58v38.08c0 1.31-.25 2.92-.04 4.2.2 1.16.83 1.76 1.07 2.92.2.96-.19 2.06-.07 3.04.15 1.3.84 2.24 1.09 3.51.27 1.35-.13 3.01-.09 4.39.11 3.51 1.05 6.87 1.07 10.42.05 7.56-.09 15.12-.04 22.68.02 2.67-.16 5.14-.5 7.79-.75 5.89-.08 11.84-1 17.7-1.01 6.46.61 13.32-.72 19.69-1.03 4.9-.78 9.76-.78 14.83 0 3.52.46 6.57.91 10.05.5 3.84.09 7.98.09 11.86v26.84c0 4.89.23 9.83.05 14.71-.2 5.57-1.13 11.03-1.08 16.62.05 5.02.21 10.05-.17 15.06-.4 5.18-1.51 10.24-1.72 15.44-.37 9.44-1.08 18.7-1.09 28.18-.01 17.19 0 34.37 0 51.56v22.16" />
  </symbol>
</svg>
<div class="container">
  <h1>Simple Handmade To-Do App With Vanilla JavaScript</h1>
  <form class="todo-form">
    <div class="form-wrapper">
      <input type="text" name="name" autofocus>
      <svg aria-hidden="true">
        <use xlink:href="#input"></use>
      </svg>
    </div>
    <div class="form-wrapper">
      <button type="submit">Add new task</button>
      <svg>
        <use xlink:href="#button"></use>
      </svg>
    </div>
  </form>
  <div class="todo-stats">
    <div class="total-tasks">
      Total Tasks: <span>0</span>
    </div>
    <div class="completed-tasks">
      Completed Tasks: <span>0</span>
    </div>
    <div class="remaining-tasks">
      Remaining Tasks: <span>0</span>
    </div>
    <svg>
      <use xlink:href="#stats"></use>
    </svg>
  </div>
  <ol class="todo-list"></ol>
</div>
<footer class="page-footer">
  <small>Made with <span>❤</span> by <a href="https://georgemartsoukos.com/" target="_blank">George Martsoukos</a>
  </small>
</footer>
/* RESET RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
@font-face {
  font-family: "Summer";
  src: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/Summer%20Font%20Regular.woff);
}

@font-face {
  font-family: "Summer Bold";
  src: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/Summer%20Font%20Bold.woff);
}

:root {
  --white: #fff;
  --red: #e31b23;
}

* {
  padding: 0;
  margin: 0;
  border: none;
  outline: none;
  box-sizing: border-box;
}

a {
  color: inherit;
  text-decoration: none;
}

input,
button {
  font-family: inherit;
  font-size: 100%;
  background: none;
}

[type="checkbox"] {
  position: absolute;
  left: -9999px;
}

button,
label {
  cursor: pointer;
}

ol {
  list-style: none;
}

body {
  font: 28px/1.2 "Summer Bold";
  margin: 1.5rem 0;
}

.container {
  max-width: 700px;
  padding: 0 10px;
  margin: 0 auto;
}

h1 {
  text-align: center;
  margin-bottom: 10px;
}

/* MAIN RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.todo-form .form-wrapper,
.todo-form input,
.todo-form button,
.todo-stats,
.todo-stats > div {
  position: relative;
}

.todo-form input,
.todo-form button,
.todo-stats > div {
  z-index: 1;
}

.todo-form input,
.todo-form button {
  width: 100%;
  padding: 15px;
}

.todo-form svg,
.todo-stats svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.todo-form button {
  color: var(--white);
  text-transform: uppercase;
}

.todo-stats {
  text-align: center;
  padding: 5px 10px;
  margin: 10px 0;
  color: var(--white);
}

.todo-list li {
  display: grid;
  align-items: baseline;
  grid-template-columns: auto 20px;
  grid-column-gap: 10px;
  padding: 0 10px;
}

.todo-list li + li {
  margin-top: 10px;
}

.todo-list .checkbox-wrapper {
  display: flex;
  align-items: baseline;
}

.todo-list .checkbox-wrapper label {
  display: grid;
  margin-right: 10px;
}

.todo-list .checkbox-wrapper svg {
  grid-column: 1;
  grid-row: 1;
  width: 20px;
  height: 20px;
}

.todo-list .checkbox-wrapper .checkmark {
  display: none;
}

.todo-list [type="checkbox"]:checked + label .checkmark {
  display: block;
}

.todo-list [type="checkbox"]:checked ~ span {
  text-decoration: line-through;
  opacity: 0.5;
}

.todo-list .remove-task {
  display: flex;
  padding: 2px;
}

.todo-list .remove-task svg {
  width: 16px;
  height: 16px;
}

/* MQ STYLES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
@media screen and (min-width: 600px) {
  .todo-form {
    display: grid;
    grid-template-columns: 2fr 1fr;
    grid-column-gap: 5px;
  }

  .todo-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
  }
}

/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.page-footer {
  font-size: 1.2rem;
  text-align: right;
  padding-right: 20px;
  margin-top: 20px;
}

.page-footer span {
  color: var(--red);
}
const todoForm = document.querySelector(".todo-form");
const todoList = document.querySelector(".todo-list");
const totalTasks = document.querySelector(".total-tasks span");
const completedTasks = document.querySelector(".completed-tasks span");
const remainingTasks = document.querySelector(".remaining-tasks span");
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];

if (localStorage.getItem("tasks")) {
  tasks.map((task) => {
    createTask(task);
  });
}

// submit form
todoForm.addEventListener("submit", function (e) {
  e.preventDefault();
  const input = this.name;
  const inputValue = input.value;

  if (inputValue != "") {
    const task = {
      id: new Date().getTime(),
      name: inputValue,
      isCompleted: false
    };

    tasks.push(task);
    localStorage.setItem("tasks", JSON.stringify(tasks));
    createTask(task);
    todoForm.reset();
  }
  input.focus();
});

// remove task
todoList.addEventListener("click", (e) => {
  if (
    e.target.classList.contains("remove-task") ||
    e.target.parentElement.classList.contains("remove-task")
  ) {
    const taskId = e.target.closest("li").id;
    removeTask(taskId);
  }
});

// update task - change status or name
todoList.addEventListener("input", (e) => {
  const taskId = e.target.closest("li").id;
  updateTask(taskId, e.target);
});

// prevent new lines with Enter
todoList.addEventListener("keydown", function (e) {
  if (e.keyCode === 13) {
    e.preventDefault();
  }
});

// create task
function createTask(task) {
  const taskEl = document.createElement("li");
  taskEl.setAttribute("id", task.id);
  const taskElMarkup = `
    <div class="checkbox-wrapper">
      <input type="checkbox" id="${task.name}-${task.id}" name="tasks" ${
    task.isCompleted ? "checked" : ""
  }>
      <label for="${task.name}-${task.id}">
        <svg class="checkbox-empty">
          <use xlink:href="#checkbox_empty"></use>
        </svg>
        <svg class="checkmark">
          <use xlink:href="#checkmark"></use>
        </svg>
      </label>
      <span ${!task.isCompleted ? "contenteditable" : ""}>${task.name}</span>
    </div>
    <button class="remove-task" title="Remove ${task.name} task">
      <svg>
        <use xlink:href="#close"></use>
      </svg>
    </button>
  `;
  taskEl.innerHTML = taskElMarkup;
  todoList.appendChild(taskEl);
  countTasks();
}

// remove task
function removeTask(taskId) {
  tasks = tasks.filter((task) => task.id !== parseInt(taskId));
  localStorage.setItem("tasks", JSON.stringify(tasks));
  document.getElementById(taskId).remove();
  countTasks();
}

// update task
function updateTask(taskId, el) {
  const task = tasks.find((task) => task.id === parseInt(taskId));

  if (el.hasAttribute("contentEditable")) {
    task.name = el.textContent;
  } else {
    const span = el.nextElementSibling.nextElementSibling;
    task.isCompleted = !task.isCompleted;
    if (task.isCompleted) {
      span.removeAttribute("contenteditable");
      el.setAttribute("checked", "");
    } else {
      el.removeAttribute("checked");
      span.setAttribute("contenteditable", "");
    }
  }
  localStorage.setItem("tasks", JSON.stringify(tasks));
  countTasks();
}

function countTasks() {
  totalTasks.textContent = tasks.length;
  const completedTasksArray = tasks.filter((task) => task.isCompleted === true);
  completedTasks.textContent = completedTasksArray.length;
  remainingTasks.textContent = tasks.length - completedTasksArray.length;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.