Tworzenie i usuwanie elementów

Potrafimy już poruszać się po elementach, potrafimy je pobrać, przechodzić z jednego na drugi itp. Czas zacząć je tworzyć a potem usuwać. Nie jest to wcale takie trudne. Wystarczy, że skorzystamy z kilku podstawowych metod oferowanych przez Javascript.

Tworzenie obiektu za pomocą createElement

Aby utworzyć nowy element na stronie korzystamy z metody document.createElement(typ). Po utworzeniu nowego elementu musimy ustawić jego właściwości.


const el = document.createElement('div');

el.id = 'myDiv';
el.innerText = "Tekst w divie";
el.setAttribute('title', 'To jest tekst w dymku');
el.classList.add('module');
el.style.setProperty('background-color', '#FF6633');

Wstawianie elementu za pomocą appendChild

Ale to nie wszystko. Utworzony w powyższy sposób element jest dostępny dla skryptu, ale nie ma go jeszcze w drzewie dokumentu. Musimy go tam wstawić metodą parentElement.appendChild(nowyElement):


const el = document.createElement('div');

el.id = 'myDiv';
el.innerText = "Tekst w divie";
el.setAttribute('title', 'To jest tekst w dymku');
el.classList.add('module');
el.style.setProperty('background-color', '#FF6633');

const div = document.querySelector('.test-first'); //pobieramy miejsce docelowe
div.appendChild(el); //wstawiamy element do drzewa dokumentu

No dobrze. Ale po co takie dzikie kombinowanie, skoro mogę po prostu użyć innerHTML i wszystko wpisać z palca, tak jakbym pisał html?


const elHtml = '<div id="bloczek" title="To jest tekst w dymku" class="uber-element" style="background-color:#FF6633">Tekst w divie</div>'

const div = document.querySelector('.test-first');
div.innerHTML = elHtml

Faktycznie za pomocą innerHTML wstawiasz kod html w dany element. Ale czy właściwość ta daje ci referencję do elementów we wstawianym html? Bardzo często będziesz chciał za chwilę zrobić jakąś akcję na wstawianych elementach - np podpiąć im kliknięcie, zmienić im tekst itp. Jeżeli będziesz korzystał z innerHTML, będziesz musiał te elementy po wstawieniu dodatkowo pobrać - np za pomocą querySelector. Nie zawsze jest to wygodniejsze.

Wstawianie elementu za pomocą insertBefore

Do wstawiania nodów w drzewo strony możemy też skorzystać z metody parentNode.insertBefore(element, beforeNode), która do parentNode wstawia element element przed elementem beforeNode:


<p id="beforeTest">
    <strong>Jestem pierwszym elementem</strong>
</p>

let counter = 0;

function insertBefore() {
    counter++;
    const p = document.querySelector('#beforeTestP'); //pobieramy element <p>
    const strong = p.firstElementChild; //pobieramy pierwsze dziecko p czyli element <strong>
    const newNode = document.createElement('span'); //tworzymy nowy nod z tekstem
    newNode.innerText = 'Inny element ' + counter;
    p.insertBefore(newNode, strong); //wstawiamy go przed <strong>
}

document.addEventListener("DOMContentLoaded", function() {
    document.querySelector('#beforeTest').addEventListener('click', insertBefore);
});

Jestem pierwszym elementem

Usuwanie elementów

Element, który istnieje w drzewie dom, możemy z niego usunąć na kilka sposobów. Możemy skorzystać np z el.remove() która usuwa el lub parentNode.removeChild(element)

Przypuśćmy, że z paragrafu z poprzedniego rozdziału chcemy usunąć znacznik strong:


<p id="paragraf">To jest <strong>bardzo</strong> ważny tekst</p>

Kilka przykładów rozwiązania powyższego zadania:


const p = document.querySelector('#paragraf');
const strong = p.querySelector('strong');

p.removeChild(strong);

//lub

strong.parentNode.removeChild(strong);

//lub

strong.remove();

Przykład użycia:


<div class="div-test-remove">
    <span>Element do usunięcia</span>
</div>
<button type="button" class="button">Usuń powyższy element</button>

const div = document.querySelector('.div-test-remove')
const btn = document.querySelector('#removeTest');
btn.addEventListener('click', function() {
    const elementToDelete = div.querySelector('span');
    div.removeChild(elementDoTelete);
});
Element do usunięcia

W przeciwieństwie do removeChild metoda remove() nie jest wspierana przez przeglądarki IE. Jeżeli musisz je wspierać, wtedy powinieneś albo użyć removeChild, albo użyć kod, który doda dla IE tą metodę:


//polyfill dla przeglądarek nie obsługujących closest
if (!Element.prototype.remove) {
    Element.prototype.remove = function() {
        this.parentElement.removeChild(this);
    }
}

Aby usunąć wszystkie dzieci danego elementu - czyli go wyczyścić, powinniśmy wykonać pętlę po jego dzieciach i wszystkie pousuwać:


const div = document.querySelector('#module');

while (div.firstChild) {
    div.removeChild(div.firstChild);
}

//lub nieco wolniejsze...
parentNode.innerHTML = '';

Przy usuwaniu elementów pamiętaj, by zmienne wskazujące na dany element też czyścić. W przeciwnym razie po usunięciu elementu mogą pozostać w pamięci dodane eventy, co wiąże się z zaśmiecaniem pamięci.
W powyższym przykładzie usuwaliśmy elementy bez podstawiania ich pod dodatkowe zmienne. Gdybyśmy takie usuwanie elementy wcześniej podstawili pod zmienną która będzie na nie wskazywać, wtedy trzeba też wyczyścić tą zmienną.


const div = document.querySelector('#module');

//usuwany element podstawiamy pod zmienna
const child = document.querySelector('span');

//dodatkowo dodajemy mu jakiś event
child.addEventListener('click', function() {
    console.log('...');
})''

//usuwamy element
div.removeChild(child);


//referencja do child pozostała w pamięci, mimo że element został usunięty
//wiec ją też czyścimy. Dzięki temu wszystkie eventy itp zostaną wraz z nią usunięte
child = null;

Zastępowanie elementów

Do zastępowania jednego elementu drugim użyjemy metody parent.replaceChild(newChild, oldChild):


<div class="div-test-replace">
    <span>Jestem starym elementem</span>
</div>
<button type="button" class="button">Zastąp spana nowym elementem</button>

const div = document.querySelector('.div-test-replace')
const btn = document.querySelector('#replaceTest');

btn.addEventListener('click', function() {
    const oldItem = div.querySelector('span');

    const newItem = document.createElement('strong');
    newItem.innerText = "Jestem nowym elementem";

    div.replaceChild(newItem, oldItem);
});
Jestem starym elementem

Tworzenie fragmentów dokumentu

Korzystając z powyższych metod z łatwością możemy tworzyć różne skomplikowane kawałki drzewa. Możemy do tego celu wykorzystać także metodę createDocumentFragment(), która tworzy fragment dokumentu. Fragment taki jest "zbiornikiem", podobnym do zwykłego diva, jednak przy wstawianiu go do dokumentu, wstawiane zostają tylko jego dzieci.

Ale po co to komu? Komu to potrzebne? Wydajności. Jeżeli wstawiamy 1000 elementów do strony, o wiele wydajniej jest je wstawić wszystkie na raz.


const fragment = document.createDocumentFragment();

for (let i=0; i<10; i++ ) {
    const p = document.createElement('p');
    p.appendChild(document.createTextNode('Akapit '+(i+1)));
    fragment.appendChild(p);
}

document.body.appendChild(fragment); //wstawiamy 10 paragrafów

I teraz pytanie do ciebie?
Czy znasz inną technikę wstawiania wielu elementów na raz?
A innerHTML znasz? Spokojnie możesz wykorzystać tą właściwość by do elementu wstawiać skomplikowany kod.
Pamiętaj tylko o tym, że używając tej metody wstawiasz html do strony, ale metoda ta nie utworzy ci automatycznie referencji... Zresztą przed chwilą o tym mówiliśmy :)

Trening czyni mistrza

Poniżej zamieszczam kilka zadań, które w ramach ćwiczenia możesz wykonać:

  1. Pobierz sobie stronę stąd (prawy klawisz na linku, zapisz jako).

    Skrypty pisz inline w kodzie strony - na końcu body.

  2. Pobierz element .list i podstaw go pod zmienną.
    Za pomocą pętli stwórz 5 elementów div i wstaw je do pobranej listy.

    Każdy stworzony div powinien mieć: - klasę .element
    - atrybut title ustawiony na "jestem divem numer ..."
    - tekst w środku "Element ..."

    W miejsce kropek wpisz kolejne numery divów

    
                    const list = document.querySelector('.list');
                    for (let i=0; i<5; i++) {
                        const div = document.createElement('div');
                        const nr = i+1;
                        div.classList.add('element');
                        div.setAttribute('title', 'Jestem divem numer ' + nr);
                        div.innerText = "Element " + nr;
    
                        list.appendChild(div);
                    }
                
  3. W kodzie masz przygotowane zdarzenie click dla buttona .delete.
    Napisz w nim treść skryptu, która usunie elementy dodane w 2 zadaniu.
    Jeżeli nie ma elementów w liście, niech wyskoczy w konsoli tekst "Nie mam co usunąć"

    
                document.querySelector('#delete').addEventListener('click', function() {
                    //tutaj napisz usuwanie elementow w liscie
                    const list = document.querySelector('.list');
                    const div =  list.querySelectorAll('div');
                    for (let i=div.length-1; i>=0; i--) {
                        div.remove();
                        //lub
                        //list.removeChild(div);
                    }
                })