Manipulacja strukturą DOM: dodawanie, usuwanie, klonowanie elementów

Manipulacja strukturą DOM: dodawanie, usuwanie, klonowanie elementów

Manipulacja strukturą DOM (Document Object Model) za pomocą JavaScript pozwala na dynamiczne modyfikowanie strony internetowej poprzez dostęp do każdego elementu HTML i jego edycję, dodawanie lub usuwanie. Kluczowe operacje to zmiana zawartości (np. innerHTMLtextContent), modyfikacja atrybutów (np. setAttribute), zmiana stylów (np. element.style.color) oraz tworzenie i usuwanie elementów (np. createElementappendChildremove).
Podstawowe operacje na DOM:

  • Wybieranie elementów:
    • document.querySelector('selektor'): Zwraca pierwszy pasujący element.
    • document.querySelectorAll('selektor'): Zwraca listę wszystkich pasujących elementów.
  • Zmiana zawartości:
    • element.innerHTML = 'nowa treść HTML': Zamienia całą zawartość elementu, włącznie z tagami HTML. Pamiętaj, że przypisanie pustego ciągu '' usuwa jej zawartość.
    • element.textContent = 'nowy tekst': Zmienia tylko sam tekst, ignorując jakiekolwiek znaczniki HTML.
  • Zmiana atrybutów:
    • element.setAttribute('atrybut', 'wartość'): Ustawia nową wartość dla istniejącego lub tworzy nowy atrybut.
    • element.getAttribute('atrybut'): Pobiera wartość podanego atrybutu.
  • Zmiana stylów:
    • element.style.właściwośćCSS = 'wartość': Bezpośrednia zmiana stylu dla danego elementu (np. element.style.color = 'blue').
  • Tworzenie i dodawanie elementów:
    • document.createElement(): Tworzy nowy, pusty element (np. document.createElement('div')).
    • elementRodzica.appendChild(nowyElement): Dodaje nowy element jako ostatnie dziecko do wskazanego Rodzica. Metoda .appendChild może przenieść element, jeśli już istnieje w DOM
    • elementRodzica.insertBefore(nowyElement, elementReferencji): Wstawia nowy element przed element referencji wewnątrz Rodzica.
  • Usuwanie elementów:
    • element.remove(): Usuwa element z jego rodzica. Metoda .remove() wywołujemy bezpośrednio na elemencie, który chcemy usunąć. Nie musimy się odwoływać do rodzica. PAMIĘTAJ! REMOVE NIC NIE ZWRACA.
    • element.removeChild() - wskazujemy rodzica, a potem w nawiasie wskazujemy który element ma być usunięty. Usunięty element otrzymujemy jako zwrot z wykonanej funkcji.

REPLACE CHILD
Metoda replaceChild() zastępuje węzeł potomny innym węzłem w obrębie węzła nadrzędnego. Działa poprzez przyjęcie dwóch argumentów: nowego węzła, który ma zastąpić stary, oraz starego węzła, który ma zostać zastąpiony. Metoda zwraca referencję do zastąpionego węzła, pozostawiając nowy węzeł w tej samej pozycji, co pierwotny. Jak działa replaceChild()

  1. Określ węzeł nadrzędny: Najpierw musisz zidentyfikować element nadrzędny, w którym znajduje się dziecko, które chcesz zastąpić.
  2. Znajdź węzeł do zastąpienia: Zidentyfikuj węzeł potomny, który ma zostać zastąpiony.
  3. Utwórz lub znajdź nowy węzeł: Stwórz nowy węzeł elementu lub znajdź istniejący, który ma zastąpić stary.
  4. Użyj metody replaceChild(): Wywołaj metodę na węźle nadrzędnym, podając jako argumenty nowy węzeł i stary węzeł.
    • Składnia: węzełNadrzędny.replaceChild(nowyWęzeł, staryWęzeł).
  5. Zwrot: Metoda zwraca zastąpiony węzeł. 

Przykład:

// 1. Znajdź rodzica
const rodzic = document.getElementById('moj-kontener');

// 2. Znajdź węzeł do zastąpienia
const staryElement = document.getElementById('stary-element');

// 3. Stwórz nowy element
const nowyElement = document.createElement('p');
nowyElement.textContent = 'To jest nowy element!';

// 4. Zastąp stary element nowym
const zastapiony = rodzic.replaceChild(nowyElement, staryElement);

// Zastąpiony węzeł jest teraz dostępny pod zmienną `zastapiony` [2].

Jak zbudować "To Do List"?
Jaka będzie kolejność budowania takiego kodu?

<body>
    <div id="kontener">
        <form>
            <label for="zadanie">Nowe zadanie:</label>
            <input type="text" id="zadanie" name="zadanie" required autocomplete="off">
            <button type="button" onclick="dodajZadanie()">Dodaj zadanie</button>
        </form>

        <ul id="listaZadan">
        </ul>
    </div>
    
<script>
const listaZadan = document.getElementById('listaZadan');
let licznikZadan = 0;
dodajZadanie = () => {
    const zadanieInput = document.getElementById('zadanie');
    const noweZadanieTekst = zadanieInput.value.trim();

    if (noweZadanieTekst !== '') {
        licznikZadan++;
        const noweZadanie = document.createElement('li');
        noweZadanie.id = `zadanie${licznikZadan}`
        const tekstZadania = document.createTextNode(noweZadanieTekst);
        const usunPrzycisk = document.createElement('button');
        usunPrzycisk.onclick = () => usunZadanie(noweZadanie);
        usunPrzycisk.textContent = 'Usuń';
        noweZadanie.appendChild(tekstZadania);
        noweZadanie.appendChild(usunPrzycisk);
        listaZadan.appendChild(noweZadanie);
        zadanieInput.value = '';
    } else {
        alert('Proszę wpisać treść zadania.');
    }
};

usunZadanie = function(zadanieElement) {
    listaZadan.removeChild(zadanieElement);
};

function usunZadanie2(zadanieId) {
    const zadanieElement = document.getElementById(zadanieId);
    if (zadanieElement) {
        listaZadan.removeChild(zadanieElement);
    }
};

</script>
</body>

SHALLOW CLONE VS DEEP CLONE

W JavaScript pojęcia "shallow clone" (klonowanie płytkie) oraz funkcja cloneNode odnoszą się do różnych mechanizmów kopiowania danych, przy czym cloneNode jest specyficzna dla elementów DOM, a klonowanie płytkie jest ogólnym sposobem kopiowania obiektów.

shallow clone klonowanie płytkie to ogólna koncepcja kopiowania obiektów, w której tworzona jest nowa instancja obiektu, ale tylko właściwości najwyższego poziomu są duplikowane

  • Jak działa: Jeśli oryginalny obiekt zawiera zagnieżdżone obiekty lub tablice, w sklonowanym obiekcie przechowywane są odniesienia (referencje) do tych samych zagnieżdżonych struktur co w oryginale.
  • Skutek: Modyfikacje zagnieżdżonych obiektów w kopii wpłyną również na oryginalny obiekt i odwrotnie, ponieważ oba obiekty wskazują na te same dane w pamięci.
  • Typowe metody: Do tworzenia płytkich kopii obiektów w JavaScript często używa się operatora rozgłoszeniowego (...) lub metody Object.assign()

Metoda cloneNode() to specyficzna funkcja API DOM (Document Object Model), służąca do klonowania węzłów drzewa DOM (np. elementów HTML). 

  • Jak działa: Przyjmuje opcjonalny parametr logiczny (true lub false), który decyduje o typie klonowania:
    • cloneNode(false): Tworzy płytką kopię węzła, co oznacza, że kopiowany jest tylko sam element wraz z jego atrybutami, ale bez elementów potomnych (dzieci) i zawartości tekstowej. Jest to domyślne zachowanie, jeśli parametr zostanie pominięty.
    • cloneNode(true): Tworzy głęboką kopię węzła, co oznacza, że klonowany jest element wraz ze wszystkimi jego atrybutami, zagnieżdżonymi elementami potomnymi i ich zawartością. 

Podsumowanie różnic

Cecha 
shallow clone(ogólna koncepcja)
cloneNode(metoda DOM)
ZastosowanieKopiowanie ogólnych obiektów i tablic w JavaScript.Klonowanie węzłów drzewa DOM (elementów HTML, tekstu itp.).
Działanie na zagnieżdżonych danychKopiuje tylko referencje do zagnieżdżonych obiektów/tablic.Zachowanie zależy od argumentu: false pomija potomków, true klonuje ich (głęboko).
Metody implementacjiOperator rozgłoszeniowy (...), Object.assign().Wbudowana metoda dostępna na obiektach węzłów DOM.

Podstawowa różnica polega na zakresie zastosowania oraz domyślnym zachowaniu w kontekście zagnieżdżonych struktur: shallow clone w kontekście obiektów JS zawsze udostępnia referencje do zagnieżdżonych danych, podczas gdy cloneNode(true) tworzy w pełni niezależną kopię całego poddrzewa DOM. 

CLONE NODE
Aby użyć cloneNode() w JavaScript, wywołaj tę metodę na elemencie, który chcesz sklonować, przekazując true, aby skopiować go wraz z dziećmi, lub false (lub pomijając argument), aby skopiować tylko element. Sklonowany element można następnie dodać do dokumentu za pomocą metod takich jak appendChild(). Kroki do użycia cloneNode()

  1. Wybierz element do sklonowania: Użyj metod takich jak document.getElementById() lub document.querySelector() do pobrania elementu, który chcesz skopiować.
  2. Sklonuj element: Wywołaj metodę cloneNode() na wybranym elemencie.
    • Aby skopiować cały element z jego potomkami (w tym tekst i inne elementy), przekaż true jako argument: let sklonowanyElement = oryginalnyElement.cloneNode(true);.
    • Aby skopiować tylko sam element (bez potomków), przekaż false lub pomiń argument: let sklonowanyElement = oryginalnyElement.cloneNode(false);.
  3. Dodaj sklonowany element do dokumentu: Użyj metod takich jak appendChild()prepend()insertBefore() lub insertAdjacentElement(), aby wstawić nową kopię do drzewa DOM. 

Przykład:

// 1. Pobierz oryginalny element
const oryginalnyElement = document.getElementById('moj-element');

// 2. Sklonuj element wraz z jego potomkami
const sklonowanyElement = oryginalnyElement.cloneNode(true);

// 3. Dodaj sklonowany element do dokumentu (np. jako dziecko ostatniego elementu w div)
document.getElementById('kontener').appendChild(sklonowanyElement);

Ważne uwagi

  • cloneNode() nie kopiuje nasłuchujących zdarzeń (event listenerów), które zostały dodane do oryginalnego elementu. Aby przywrócić funkcjonalność, musisz ponownie podpiąć te zdarzenia do sklonowanego elementu.
  • Metoda kopiuje wszystkie atrybuty i ich wartości.
  • Kopiowanie wraz z dziećmi (głębokie klonowanie) jest przydatne, gdy chcesz dokładnie odtworzyć strukturę elementu

STYLOWANIE ELEMENTÓW W DOM

classList.toggle() to metoda w JavaScript, która przełącza (dodaje lub usuwa) jedną lub więcej klas CSS z elementu, na którym jest wywoływana. Jeśli klasa istnieje, jest usuwana; jeśli jej nie ma, jest dodawana. Umożliwia to łatwe zmienianie wyglądu lub zachowania elementu w reakcji na zdarzenia, na przykład przy kliknięciu przycisku. Jak działa classList.toggle()

  • Dodawanie: Jeśli element nie ma określonej klasy, toggle() ją dodaje.
  • Usuwanie: Jeśli element już posiada tę klasę, toggle() ją usuwa.
  • Przełączanie: Ta akcja następuje za każdym razem, gdy metoda jest wywoływana, co działa jak przełącznik.
  • Przykład: Można jej użyć np. do włączenia/wyłączenia widoczności ukrytego menu, zmiany koloru przycisku po kliknięciu, czy do przełączania stanu „polubienia”. 

Przykład użycia:

// Pobierz element z id="myButton"
const myButton = document.getElementById("myButton");

// Dodaj nasłuchiwanie na zdarzenie kliknięcia
myButton.addEventListener("click", function() {
  // Przełącz klasę "active" na tym przycisku
  myButton.classList.toggle("active");
});

W tym przykładzie, przy każdym kliknięciu na przycisk myButton, jego klasa będzie przełączana między „active” a brakiem tej klasy, co może być użyte do zmiany jego wyglądu w CSS. 


DOCUMENT FRAGMENT

Kontener .createDocumentFragment() istnieje poza drzewem DOM, a służy do budowaniu złożonych struktur w pamięci, a potem jednorazowo wstawianie je do dokumentu.

DocumentFragment w JavaScript to obiekt będący lekkim węzłem DOM, który służy do tymczasowego przechowywania elementów HTML bez modyfikowania drzewa DOM w czasie rzeczywistym. Jego głównym celem jest poprawa wydajności, ponieważ pozwala na budowanie skomplikowanej struktury elementów w pamięci, a następnie dodanie jej do dokumentu za jednym razem. Główne cechy i zastosowania:

  • Wydajność: Tworzenie i manipulowanie elementami w DocumentFragment jest szybsze niż bezpośrednie dodawanie ich do dokumentu, ponieważ przeglądarka nie musi renderować ani przepracowywać zmian przy każdej pojedynczej operacji.
  • Tymczasowy kontener: Działa jak tymczasowy, wirtualny kontener. Kiedy dodajesz DocumentFragment do głównego drzewa DOM, wstawiane są jego dzieci, a sam fragment znika z drzewa DOM.
  • Typowe zastosowanie: Jest użyteczne, gdy trzeba dodać do strony wiele elementów (np. listę). Możesz najpierw dodać wszystkie elementy do DocumentFragment, a następnie wstawić cały fragment do DOM za pomocą jednej operacji, co znacznie skraca czas operacji.