JavaScript 21.11

Manipulacja DOM

const paragraf = document.createElement('p') tworzy element który jest przechowywany tylko w pamięci, jeszcze nie dodany do drzewa DOM, ten p paragraf nie daje nam treści w środku. metoda creatElement tworzy element HTML. Element jako kontener

createTextNode pozwala na wypełnienie elementu który stworzyliśmy przez crateElement. Tekst jako zawartość kontenera.

Można użyć inner text inner html lub dodać do paragrafu setAttibute('id','nowyparagraf') a następnie get element by id i modyfikować w dowolny sposób(zamiast createTextNode).

paragraf.appendChild(tekst) do paragrafu dodaj tekst

document.getElementById('kontener').appendChild(paragraf); do kontenera dodajemy tekst

 <div id="kontener"></div>
    <button onclick="dodajParagraf()">Dodaj paragraf</button>
    <button onclick="dodajListeZTekstem()">Dodaj listę</button>

    <script>
        // Przykład 1: Tworzenie prostego paragrafu z tekstem
        function dodajParagraf() {
            // createElement tworzy nowy element <p> w pamięci
            const paragraf = document.createElement('p');
            // setAttribute ustawia atrybut klasy dla paragrafu
            // paragraf.setAttribute('id', 'nowy-paragraf');

            // createTextNode tworzy węzeł tekstowy
            const tekst = document.createTextNode('To jest nowy paragraf utworzony dynamicznie');
            
            // appendChild dodaje tekst do paragrafu (paragraf wciąż w pamięci)
            paragraf.appendChild(tekst);
            
            // Dopiero teraz paragraf jest dodany do dokumentu i staje się widoczny
            document.getElementById('kontener').appendChild(paragraf);
        }

        // Przykład 2: Tworzenie złożonej struktury - lista z elementami
        function dodajListeZTekstem() {
            // Tworzymy kontener listy
            const lista = document.createElement('ul');
            
            // Tworzymy trzy elementy listy
            const element1 = document.createElement('li');
            const element2 = document.createElement('li');
            const element3 = document.createElement('li');
            
            // Tworzymy węzły tekstowe dla każdego elementu
            const tekst1 = document.createTextNode('Pierwszy element listy');
            const tekst2 = document.createTextNode('Drugi element listy');
            const tekst3 = document.createTextNode('Trzeci element listy');
            
            // Dodajemy teksty do odpowiednich elementów li
            element1.appendChild(tekst1);
            element2.appendChild(tekst2);
            element3.appendChild(tekst3);
            
            // Dodajemy wszystkie li do ul (wszystko wciąż w pamięci)
            lista.appendChild(element1);
            lista.appendChild(element2);
            lista.appendChild(element3);
            
            // Ostatecznie dodajemy całą strukturę do dokumentu
            document.getElementById('kontener').appendChild(lista);
        }

        // Przykład 3: Demonstracja że createElement nie dodaje do DOM automatycznie
        const niewidocznyElement = document.createElement('div');
        const niewidocznyTekst = document.createTextNode('Ten element istnieje tylko w pamięci JavaScript');
        niewidocznyElement.appendChild(niewidocznyTekst);
        // Element istnieje w zmiennej ale nie jest widoczny w przeglądarce
        // Sprawdź w konsoli: niewidocznyElement.textContent zwróci tekst
    </script>

appendChild()- dodaj na koniec, ostatnie dziecko do wskazanego rodzica. Jeśli istnieje już element w DOM o konkretnej nazwie i uchwycie to appendChild go nie tworzy, nie kopiuje tylko przenosi. Przenosi pierwszy na koniec, pierwszy np. paragraf zostanie usunięty z jego pozycji i zostanie dodany na końcu. samo appendChild bez wykorzystania document.getElementbyId nie dodaje elementów do DOM, dopóki nie wskażemy gdzie to dodać to jest w pamięci

insertBefore() - wstaw przed drugim, wstaw przed wskazanym elementem, musimy sięgnąć do istniejącego paragrafu, składania jest inna elementdoktóregoodnosimysie(rodzic).insertBefore(gdzie wstawić nowy element, przed jakim elementem który istnieje), jeżeli nie wskazujemy punktu odniesienia to inserBefore zadziała jak appenChild, domyslnie doda na końcu

replaceChild() - zmienia istniejący element nowym, ma taką samą składnie co insertBefore - wskazujemy nowy element i wskazujemy element odniesienia, jeżeli nie ma tego drugiego elementu odniesienia = błąd, najpierw nowy potem stary

removeChild() - po prostu usuwa element który wymaga wywołania rodzica i przekazania które dziecko z niego trzeba usunąć const usunietyElement=rodzic.removeChild(paragraf); w metodzie removeChild dostajemy ten element jako zwrot z wykonanej funkcji. to oznacza, ze my ten element jesteśmy w stanie wykorzystać, możemy na nim potem zrobić appendchild insertbefore itd.

remove- nowsza metoda ES6, nie wymaga odniesienia do rodzica, wywołujemy bezpośrednio na elemencie który chcemy usunąć. element.remove, nie zwraca nic bo cały element został usunięty i nie ma rodzica, który miałby do niego referencje.

 <div id="lista">
        <p id="p1">Paragraf pierwszy</p>
        <p id="p2">Paragraf drugi</p>
        <p id="p3">Paragraf trzeci</p>
    </div>
    
    <button onclick="demonstracjaAppendChild()">appendChild - dodaj na koniec</button>
    <button onclick="demonstracjaInsertBefore()">insertBefore - wstaw przed drugim</button>
    <button onclick="demonstracjaReplaceChild()">replaceChild - zamień drugi</button>
    <button onclick="demonstracjaPrzeniesienia()">Przenieś pierwszy na koniec</button>

    <script>
        // Przykład 1: appendChild dodaje element na końcu listy dzieci
        function demonstracjaAppendChild() {
            const nowyParagraf = document.createElement('p');
            const tekst = document.createTextNode('Nowy paragraf dodany na końcu przez appendChild');
            nowyParagraf.appendChild(tekst);
            
            // appendChild dodaje element jako ostatnie dziecko
            const lista = document.getElementById('lista');
            lista.appendChild(nowyParagraf);
        }

        // Przykład 2: insertBefore wstawia element przed wskazanym elementem
        function demonstracjaInsertBefore() {
            const nowyParagraf = document.createElement('p');
            const tekst = document.createTextNode('Paragraf wstawiony przed drugim paragrafem');
            nowyParagraf.appendChild(tekst);
            
            const lista = document.getElementById('lista');
            const drugiParagraf = document.getElementById('p2');
            
            // insertBefore(co, przed_czym) - wstawia nowy element przed elementem odniesienia
            lista.insertBefore(nowyParagraf, drugiParagraf);
        }

        // Przykład 3: replaceChild zamienia istniejący element nowym
        function demonstracjaReplaceChild() {
            const nowyParagraf = document.createElement('p');
            const tekst = document.createTextNode('Ten paragraf zastąpił drugi paragraf');
            nowyParagraf.appendChild(tekst);
            
            const lista = document.getElementById('lista');
            const drugiParagraf = document.getElementById('p2');
            
            // replaceChild(nowy, stary) - zamienia stary element nowym
            // UWAGA: kolejność parametrów - najpierw nowy potem stary
            lista.replaceChild(nowyParagraf, drugiParagraf);
        }

        // Przykład 4: appendChild przenosi element jeśli już istnieje w DOM
        function demonstracjaPrzeniesienia() {
            const lista = document.getElementById('lista');
            const pierwszyParagraf = document.getElementById('p1');
            
            // appendChild na istniejącym elemencie PRZENOSI go a nie kopiuje
            // Pierwszy paragraf zostanie usunięty z jego obecnej pozycji
            // i dodany na końcu listy
            lista.appendChild(pierwszyParagraf);
        }
    <div id="kontener">
        <p id="p1">Paragraf 1 - usuń przez removeChild</p>
        <p id="p2">Paragraf 2 - usuń przez remove</p>
        <p id="p3">Paragraf 3 - usuń i przywróć</p>
        <p id="p4">Paragraf 4 - usuń wszystkie dzieci</p>
    </div>
    
    <button onclick="usunRemoveChild()">Usuń removeChild</button>
    <button onclick="usunRemove()">Usuń remove</button>
    <button onclick="usunIPrzywroc()">Usuń i przywróć</button>
    <button onclick="usunWszystkieDzieci()">Usuń wszystkie dzieci</button>

    <script>
        // Przykład 1: removeChild - starsza metoda wymagająca rodzica
        function usunRemoveChild() {
            const paragraf = document.getElementById('p1');
            const rodzic = document.getElementById('kontener');
            
            // removeChild wywołujemy na rodzicu przekazując dziecko
            // Metoda zwraca usunięty element
            const usunietyElement = rodzic.removeChild(paragraf);
            console.log('Usunięty element:', usunietyElement.textContent);
        }

        // Przykład 2: remove - nowsza prostsza metoda
        function usunRemove() {
            const paragraf = document.getElementById('p2');
            const tempElement = paragraf;
            // remove wywołujemy bezpośrednio na elemencie do usunięcia
            // Nie potrzebujemy odniesienia do rodzica - dużo prostsze!
            paragraf.remove();
            return (tempElement);
        }

        // Przykład 3: removeChild zwraca element który można przywrócić
        let zapamietanyElement = null;
        function usunIPrzywroc() {
            const paragraf = document.getElementById('p3');
            const kontener = document.getElementById('kontener');
            
            if (paragraf) {
                // Usuwamy i zapisujemy w zmiennej
                zapamietanyElement = kontener.removeChild(paragraf);
                console.log('Element usunięty i zapisany');
            } else if (zapamietanyElement) {
                // Przywracamy zapisany element
                kontener.appendChild(zapamietanyElement);
                zapamietanyElement = null;
                console.log('Element przywrócony');
            }
        }

        // Przykład 4: Usuwanie wszystkich dzieci elementu w pętli
        function usunWszystkieDzieci() {
            const kontener = document.getElementById('kontener');
            
            // Usuwamy wszystkie dzieci w pętli while
            // Używamy firstChild bo po każdym usunięciu lista dzieci się zmienia
            while (kontener.firstChild) {
                kontener.removeChild(kontener.firstChild);
            }
            
            // Alternatywnie można użyć innerHTML = '' ale to mniej "czysty" sposób
        }
      

Zadanie: utwórz listę zadań, za pomocą w.w elementów, Usuwanie na dwa spsoby, jeden przez removechild a drugi przez funkcje dzięki której będziemy mieć dostęp do wartości jeśli ustawimy licznik i id dla kazdego nowego li.

<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);
    }
};

Klonowanie

shallowClone() - płytka kopia, daje pusty kontener, szybsze

deepClone() - id przestaje być unikalne, kopiuje wraz z id, jakbyśmy chcieli zrobić remove przez getElementById to nie jest dobre rozwiązanie bo moze triggerowac zdarzenia nie przezaczone dla niego. Klon powinien dostać nowe id klon.id = 'klon'; przydaje się dużo bardziej niz shallowClone, kopiuje cały element z całym poddrzewem dzieci. wolniejsze

Style

style property dobre dla dynamicznych wartości liczbowych, dynamiczny rozmiar div, dynamiczne rzeczy, notacja w camelCase, wartości są string razem z jednostką. definiowane osobno dla elementu, nie dla wszystkich

elemet.style.color = 'red'; styl inline dla konkretnego obiektu w DOM, lepsze jest używanie klas niż inline

Class list, predefiniowane style w CSS:

dodawanie klasy element.classList.add używamy aby przypisać do obiektu

usuwanie klasy

toggle - włączyć/wyłączyć, nie trzeba logiki wystarczy ze jeśli dany element ma tą klasę to usunie ten element, skoro nie ma to zostawi. Zwraca boolean, true false czy klasa została dodana czy nie.

Document fragment

lekki kontener który istnieje poza drzewem DOM a służy do budowania złożonych struktur w pamięci a potem jednorazowo je wstawić do dokumentu. Za dużo appendChild to nie optymalne, const fragment = document.createDocumentFragment(); return fragment;


Event Handling

Bread and Butter interakwtyności w JS

Eventy to sygnały, że coś się wydarzyło: kliknięty przycisk, że jakiś element ma nad sobą kursor itd.

onlick ma tylko jedną funkcję, jeżeli stworzymy element i temu elementowi damy onlick to tylko takie pojedyncze zdarzenie będzie rejestrowane przez przeglądarkę. Często się nadużywa onclick. Jeśli stworzymy funkcję onclick a potem drugą to te funkcje się nadpisują, może być tylko jedna zdefiniowana funkcja dla onclick

addEventListener - "podsłuchują" co użytkownik robi, wykonują się wszystkie funkcje, nie nadpisują się. Szereg zdarzeń, funkcji użyć tego. Kliknięcie wykona obie funkcje w kolejności dodania. Nawet dla jednej funkcji używać tego, nie onclick tak jest wygodniej bo istnieje szansa ze w którymś momencie onclick zostanie usunięte z obsługi JS

logujKlinięcie(event){} (lub e) i jedno i drugie oznacza zdarzenie, jesteśmy w stanie sprawdzić który element wywołał zdarzenie to pozwala mieć jeden listener na całą grupę obiektów

Funkcja obsługująca event otrzymuje obiekt typ nazwa eventu(co się wydarzyło, co wciśnięto) target jaki element faktycznie striggerrował ten event (kto wywołał), current target pokazuje id (gdzie został przypisany listener).

Po co to jest: zdefiniować div i na niego listener a potem tworzenie przycisków np 100, i jestem w stanie je wszystkie obsługiwać tym jednym event listenerem, nie musze do każdego button przypisywać. Jeśli mamy id to te przyciski mogą mieć jakąś inną interakcje między sobą


stopPropagation() — zatrzymuje propagację zdarzenia
Powoduje, że zdarzenie nie przechodzi dalej w górę (bubbling) lub w dół (capturing) drzewa DOM.

Klikasz w element A znajdujący się w elemencie B.

Normalnie kliknięcie dociera do A → potem do B (bubbling).

Gdy w A wywołasz event.stopPropagation(), klik zatrzyma się na A i nie uruchomi handlerów w B.

preventDefault() — blokuje domyślne zachowanie elementu, Powstrzymuje przeglądarkę przed wykonaniem domyślnej akcji powiązanej z danym elementem.

Przykłady domyślnych akcji:

kliknięcie w link <a> → przejście na inną stronę

kliknięcie w <input type="checkbox"> → zaznaczenie/odznaczenie

wysłanie formularza <form>

kliknięcie prawym przyciskiem → menu kontekstowe

preventDefault() → blokuje domyślne działanie elementu.

stopPropagation() → blokuje przechodzenie zdarzenia do innych elementów.

Mouse Events

mousedown - W momencie naciśnięcia przycisku myszy — zanim go puścisz.

mouseup - W momencie puszczenia przycisku myszy

click- W momencie szybkiego wciśnięcia i puszczenia lewego przycisku myszy na tym samym elemencie. click = mousedown + mouseup (w krótkim czasie)

dblclick - Po wykonaniu dwóch szybkich kliknięć (double click) na tym samym elemencie.

Kliknięcie generuje zdarzenia w takiej kolejności:

mousedown

mouseup

click

Dla podwójnego kliknięcia:

mousedown

mouseup

click

(ponownie) mousedown

mouseup

click

dblclick

Pozycja myszy:

event.clientX, event.clientY – pozycja względem okna

event.pageX, event.pageY – pozycja względem całej strony

Jaki przycisk myszy?

event.button

0 – lewy

1 – środkowy

2 – prawy

Klawisze modyfikujące:

event.ctrlKey

event.shiftKey

event.altKey

event.metaKey (Cmd na Mac)

mousemove - Za każdym razem, gdy użytkownik porusza myszą nad danym elementem. Częstotliwość: bardzo wysoka (dziesiątki–setki razy na sekundę).

mouseenter - W momencie, gdy kursor wchodzi do elementu. nie propaguje się (nie bubble’uje) wywołuje się tylko raz, przy wejściu, nawet jeśli poruszasz się wewnątrz elementu

mouseleave - Gdy kursor opuszcza element.

mouseover - Podobnie do mouseenter, ale: propaguje się (bubble’owanie), wywoływane również wtedy, gdy wchodzisz na dzieci elementu
(czyli jest bardziej "czułe" niż mouseenter)

mouseout - Gdy kursor wychodzi poza element lub jego dzieci.

clientX, clientY – pozycja względem okna

pageX, pageY – pozycja względem dokumentu

movementX, movementY – zmiana pozycji od ostatniego eventu

relatedTarget (dla mouseenter/mouseleave) – element, z którego wchodzisz/wychodzisz

Keyboard events

Keyboard Events w JavaScript

JavaScript udostępnia trzy podstawowe zdarzenia klawiatury:

keydown

keypress (przestarzałe – NIE używać w nowych projektach)

keyup

keydown - W momencie wciśnięcia klawisza (zaraz gdy zaczniesz naciskać). Odpala się od razu, nawet zanim klawisz zostanie puszczony.

keypress (deprecated)Podczas naciśnięcia klawisza wprowadzającego znak (np. litery, cyfry). Ten event jest przestarzały (deprecated), ma złą kompatybilność i nie powinien być używany.

keyup - Gdy klawisz zostaje puszczony.

Kluczowe właściwości obiektu event:

e.key - Zwraca „co wpisujesz”, czyli symbol albo nazwa klawisza.

Przykłady:

"a"

"A"

"Enter"

"Shift"

"ArrowLeft"

e.code

Zwraca fizyczny klawisz na klawiaturze (nie zależy od języka klawiatury).

"KeyA"

"Digit1"

"Space"

"ArrowUp"

Używaj e.code do sterowania w grach, bo nie zmienia się przy zmianie układu klawiatury.

Modifier keys (prawda/fałsz):

e.shiftKey

e.altKey

e.ctrlKey

e.metaKey (Mac: Command)

Focus Events

Istnieją cztery podstawowe zdarzenia dotyczące fokusu:

focus

blur

focusin

focusout

focus - Gdy element otrzyma fokus (np. po kliknięciu w input lub przejściu tabulatorem). nie bubble’uje (nie propaguje się w górę DOM)

blur - Gdy element traci fokus (np. klikniesz poza input). również nie bubble’uje, Typowe zastosowania:

walidacja po opuszczeniu pola

ukrywanie tooltipów

zapisywanie wartości

focusin - Gdy fokus wchodzi do elementu, ale bubble’uje (w przeciwieństwie do focus). Dzięki temu można nasłuchiwać na całym kontenerze, zamiast na każdym input oddzielnie.

focusout - Gdy fokus wychodzi z elementu, i również bubble’uje.