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.