Lista zadań: model w klasie, widok z modelu

Klasy i obiekty - JavaScript od podstaw: konsola (HTML/CSS/JS)

Większy komponent: klasa `TodoList` trzyma model (tablica zadań z polem `done`) i ma metodę `render`, która z modelu odbudowuje listę `<ul>`. Każda akcja użytkownika (dodanie, kliknięcie, usunięcie) zmienia TYLKO model i woła `render`. Dzięki temu widok nigdy nie rozjedzie się z danymi. Kliknięcia obsługuje jeden nasłuchiwacz na liście (delegowanie), a numer zadania niesie atrybut `data-index`.

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="utf-8">
  <title>Lista zadań</title>
  <style>
    body { font-family: sans-serif; max-width: 34em; }
    .counter { display: inline-flex; gap: 0.5em; align-items: center; border: 1px solid #94a3b8; border-radius: 6px; padding: 0.4em 0.7em; margin: 0.3em; }
    .counter .value { min-width: 2em; text-align: center; font-weight: bold; }
    button, input { font: inherit; padding: 0.2em 0.6em; }
    #list li { cursor: pointer; margin: 0.2em 0; }
    #list li.done { text-decoration: line-through; color: #64748b; }
  </style>
</head>
<body>
  <h1>Do zrobienia</h1>
  <input id="new-task" placeholder="Nowe zadanie">
  <button id="add">Dodaj</button>
  <ul id="list"></ul>
  <p id="count"></p>
  <script>
    class TodoList {
      constructor(list, count) {
        this.tasks = [];
        this.list = list;
        this.count = count;
        list.addEventListener('click', e => {
          const li = e.target.closest('li');
          if (!li) return;
          const index = Number(li.dataset.index);
          if (e.target.matches('.remove')) this.remove(index);
          else this.toggle(index);
        });
      }

      add(text) {
        if (text.trim() === '') return;
        this.tasks.push({ text: text.trim(), done: false });
        this.render();
      }

      toggle(index) {
        this.tasks[index].done = !this.tasks[index].done;
        this.render();
      }

      remove(index) {
        this.tasks.splice(index, 1);
        this.render();
      }

      render() {
        this.list.replaceChildren();
        this.tasks.forEach((task, i) => {
          const li = document.createElement('li');
          li.dataset.index = i;
          li.textContent = task.text + ' ';
          if (task.done) li.classList.add('done');
          const remove = document.createElement('button');
          remove.className = 'remove';
          remove.textContent = 'usuń';
          li.appendChild(remove);
          this.list.appendChild(li);
        });
        const left = this.tasks.filter(t => !t.done).length;
        this.count.textContent = `Do zrobienia: ${left} z ${this.tasks.length}`;
      }
    }

    const todo = new TodoList(document.getElementById('list'), document.getElementById('count'));
    const field = document.getElementById('new-task');
    document.getElementById('add').addEventListener('click', () => {
      todo.add(field.value);
      field.value = '';
    });
    todo.add('Odrobić matematykę');
    todo.add('Spakować plecak');
    todo.toggle(0);
  </script>
</body>
</html>

Ładowanie interaktywnego edytora... Jeśli się nie pojawi, włącz JavaScript.

Wszystkie lekcje kursu JavaScript od podstaw: konsola·Zaloguj się, aby zapisać postęp na koncie.