← Lekcje
JavaScript · lekcja 24

DOM 5/6: dodajemy nowy element na stronę

Plan tematu (6 godzin):

  1. Godzina 1: DOM (obiektowy model dokumentu) - strona jako drzewo obiektów
  2. Godzina 2: znajdujemy element na stronie
  3. Godzina 3: zmieniamy tekst i wygląd elementu
  4. Godzina 4: podmieniamy atrybut - src, class
  5. Godzina 5 (dziś): dodajemy nowy element na stronę
  6. Godzina 6: powtórka - łączymy wyszukiwanie, zmianę i tworzenie
Rozgrzewka · 3 minuty

Rozgrzewka: atrybuty z poprzedniej lekcji

  1. Co robi setAttribute("src", "picture2.jpg") wywołane na elemencie <img>?
  2. Jak odczytacie wartość atrybutu src metodą getAttribute?
  3. Jak zmienicie klasę elementu poleceniem setAttribute?
Część 1 · Tworzymy element

createElement: nowy element, jeszcze poza drzewem

Do tej pory pracowaliście na elementach, które już były na stronie. document.createElement("li") tworzy zupełnie nowy element - jak wyjęcie nowego klocka z pudełka. Macie go w rękach, ale nie jest jeszcze wpięty w żadną konstrukcję: nie widać go na stronie, dopóki go gdzieś nie dołączycie.

const newItem = document.createElement("li");
ul#tasks li Pierwsze zadanie w drzewie DOM li newItem document.createElement("li") jeszcze nie w drzewie
MDN, Web/API/Document/createElement: „The createElement() method of the Document interface creates a new HTMLElement that has the specified localName.” - „Metoda createElement() interfejsu Document tworzy nowy obiekt HTMLElement o podanej nazwie localName.” (tłum. własne)
Część 2 · Dołączamy element

appendChild: dołączamy węzeł do rodzica

Sam createElement nie wystarczy - element trzeba jeszcze dołączyć do drzewa. list.appendChild(newItem) dołącza newItem jako ostatnie dziecko elementu list, tak jak dołączenie nowego wagonu na koniec pociągu. Dopiero po tym element pojawia się na stronie.

list.appendChild(newItem);
PRZED PO ul#tasks li: Pierwsze zadanie newItem (li) jeszcze niedołączony ul#tasks li: Pierwsze zadanie li: newItem appendChild(newItem)
MDN, Web/API/Node/appendChild: „The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.” - „Metoda appendChild() interfejsu Node dodaje węzeł na koniec listy dzieci podanego węzła nadrzędnego.” (tłum. własne)
Część 3 · Wpisujemy tekst

textContent: co ma być widoczne w środku

Nowy element z createElement jest pusty w środku - to sam znacznik, bez treści. newItem.textContent = "Kupić mleko" wpisuje tekst do wnętrza elementu. Ten sam zapis znacie już z lekcji o zmianie treści (temat 3/6) - tam zmieniał tekst istniejącego elementu, tu ustawia tekst elementu, który dopiero co powstał.

const newItem = document.createElement("li");
newItem.textContent = "Kupić mleko";

Kolejność ma znaczenie tylko o tyle, że newItem musi już istnieć - samą kolejność textContent i appendChild możecie odwrócić, efekt końcowy jest taki sam.

MDN, Web/API/Node/textContent: „The textContent property of the Node interface represents the text content of the node and its descendants.” - „Właściwość textContent interfejsu Node reprezentuje zawartość tekstową węzła i jego potomków.” (tłum. własne)
Część 4 · Usuwamy element

remove(): odcinamy węzeł z drzewa

Metoda remove() wywołana na elemencie odcina go z drzewa DOM - jak odcięcie gałęzi z drzewa. Element znika ze strony. list.lastElementChild to ostatnie dziecko elementu list - w ten sposób znajdziecie ostatni element listy bez pamiętania jego id.

list.lastElementChild.remove();
ul#tasks li: Pierwsze zadanie li: Ostatnie zadanie lastElementChild.remove() usunięty
MDN, Web/API/Element/remove: „The Element.remove() method removes the element from its parent node. If it has no parent node, calling remove() does nothing.” - „Metoda Element.remove() usuwa element z jego węzła nadrzędnego. Jeśli element nie ma węzła nadrzędnego, wywołanie remove() nic nie robi.” (tłum. własne)
Część 5 · Kod razem

Kod razem: dodajemy i usuwamy zadanie z listy

page.html:

<ul id="tasks">
  <li>Pierwsze zadanie</li>
</ul>
<button type="button" onclick="addItem()">Dodaj zadanie</button>
<button type="button" onclick="removeLast()">Usuń ostatnie</button>

script.js:

function addItem() {
  const list = document.getElementById("tasks");
  const newItem = document.createElement("li");
  newItem.textContent = "Nowe zadanie";
  list.appendChild(newItem);
}

function removeLast() {
  const list = document.getElementById("tasks");
  list.lastElementChild.remove();
}
Kod zgodny ze składnią createElement/appendChild/remove (slajdy 3-6)
Egzamin EE.09

Tak pytają na egzaminie o DOM

W bankach zadań CKE nie ma na razie pytania wprost o createElement, appendChild ani remove(). Pisemne pytania o DOM (obiektowy model dokumentu) dotyczą częściej wyszukiwania elementu i zmiany jego treści - getElementById i innerHTML (tematy 2/6 i 3/6, lekcje 21 i 22).

Tworzenie i usuwanie elementów wraca za to w części praktycznej egzaminu - i w projektach na kolejnych lekcjach (gra, blog), gdzie strona sama dokłada elementy na podstawie danych.

Ćwiczenie przy komputerze · 20 minut

Ćwiczenie: dodajemy i usuwamy zadanie z listy

  1. Otwórz page.html z poprzednich lekcji. Dodaj <ul id="tasks"> z jednym elementem <li> w środku i dwa przyciski: „Dodaj zadanie” i „Usuń ostatnie”.
  2. W script.js napisz funkcję addItem(): pobierz listę po id, utwórz nowy <li> przez createElement, ustaw mu tekst przez textContent i dołącz do listy przez appendChild.
  3. Napisz funkcję removeLast(): pobierz listę po id i wywołaj remove() na jej ostatnim dziecku (lastElementChild).
  4. Sprawdź w przeglądarce: otwórz page.html, kliknij kilka razy „Dodaj zadanie”, potem „Usuń ostatnie” - lista ma rosnąć i się skracać.
  5. Dla chętnych: zapisz zmiany w swoim repozytorium - commit „Dodaje tworzenie i usuwanie elementu listy”.
Cały plik
<!-- page.html -->
<ul id="tasks">
  <li>Pierwsze zadanie</li>
</ul>
<button type="button" onclick="addItem()">Dodaj zadanie</button>
<button type="button" onclick="removeLast()">Usuń ostatnie</button>

// script.js
function addItem() {
  const list = document.getElementById("tasks");
  const newItem = document.createElement("li");
  newItem.textContent = "Nowe zadanie";
  list.appendChild(newItem);
}

function removeLast() {
  const list = document.getElementById("tasks");
  list.lastElementChild.remove();
}
Na koniec

Sprawdź się

  1. Czy element utworzony przez document.createElement("li") jest od razu widoczny na stronie?
  2. Jakiej metody użyjecie, żeby dołączyć nowo utworzony element jako ostatnie dziecko istniejącego elementu?
  3. Co się stanie, jeśli wywołacie remove() na elemencie, który nie ma rodzica (nie jest w drzewie)?

To tyle na dziś - kolejny temat: powtórka całego tematu DOM.

Poza szkołą

A jak to wygląda w produkcji?

Na lekcjiW produkcjiStatus
createElement + appendChild ręcznie dla każdego elementuFrameworki (React, Vue) budują drzewo z szablonu i danych, wywołania podobne do createElement/appendChild dzieją się w tlezastępowane
Dodawanie elementów po jednym, w pętlidocument.createDocumentFragment() - zbiera wiele nowych elementów naraz i dołącza je jednym appendChild, żeby przeglądarka nie przerysowywała strony przy każdym dodaniu (MDN zastrzega: bywa wolniejsze).nadal w użyciu
list.lastElementChild.remove()Biblioteki renderujące same usuwają węzeł, gdy odpowiadająca mu pozycja znika z danych (np. z tablicy)zastępowane

Ciekawostka z MDN: document.createDocumentFragment() tworzy pojemnik w pamięci, do którego można dołączyć wiele nowych elementów, zanim jakikolwiek z nich trafi na stronę. Gdy taki fragment dołączycie do dokumentu przez appendChild, do drzewa DOM trafiają tylko jego dzieci - sam fragment zostaje pusty.

definicje metod - slajdy 3, 4, 6 (MDN) · zastrzeżenie: developer.mozilla.org/en-US/docs/Web/API/DocumentFragment („using a DocumentFragment is slower than appending to the document in a loop”, tłum. własne: „użycie DocumentFragment bywa wolniejsze niż dodawanie do dokumentu w pętli”)