Ten wpis dotyczy sposobu w jaki możemy odwołać się do elementów HTML na stronie i nie tylko, ponieważ EventTarget dotyczy Elements, document, window, XMLHttpRequest i wiele wiecej (zerknij tutaj).
Eventy
Przeglądarka wychwyca każdy ruch, każdą czynność jaką użytkownik wykonuje na stronie. Weźmy na przykład taką stronę:

Po wejściu na stronę Google i kliknięciu w Logo, przeglądarka „rejestruje” to zdarzenie pomimo, iż nie jest wykonywana żadna funkcja, a Logo nie wchodzi w interakcję. Dlaczego to jest ważne? Ponieważ wcześniej wydawało mi się, że dopóki my nie zdefiniujemy żadnego eventu przypisanego do obiektu, to przeglądarka nie rejestruje żadnych „clicków” na tym obiekcie. Żyłem w błędzie 😉
AddEventListener
Jeżeli już wiemy, że przeglądarka rejestruje każdą naszą akcje na stronie, to teraz łatwiej będzie zrozumieć ideę nasłuchiwania. AddEventListener to nic innego jak powiedzenie przeglądarce: jeżeli wykryjesz, że użytkownik kliknie w Logo, to wtedy wykonaj poinformuj o tym również funkcję XYZ.
Przykład:
const addToCartButtons = document.querySelectorAll('.js-add-to-cart')
if (addToCartButtons != null) {
addToCartButtons.forEach(button => {
button.addEventListener("click", (event) => {
alert("Dodałem produkt do koszyka!")
})
});
}
Szukamy przycisków posiadających klasę js-add-to-cart, a następnie dla takich elementów dodajemy Listenera informującego o kliknięciu. Jeżeli kliknięcie nastąpi, zostanie wykonana funkcja anonimowa, która uruchomi alert i zostanie wyświetlona informacja: Dodałem produkt do koszyka!
Simple 😉
To teraz skomplikujmy i przeanalizujmy takie case study.
Budujemy stronę kategorii produktów. Na stronie kategorii wyświetlamy listę produktów. Strona kategorii posiada paginację, a po wybraniu kolejnej strony, produkty są ładowane asynchronicznie bez przeładowania strony. Każdy produkt posiada przycisk pozwalający na dodanie produktu do koszyka. Po kliknięciu przycisku, wysyłany jest request do bazy, request zawiera id produktu.
Po wejściu na stronę jest widoczne 16 produktów. Wybranie kolejnej strony paginacji skutkuje usunięciem widocznych produktów oraz dodaniem nowych produktów do listy. Łączna ilość stron wynosi 100, ilość produktów to 1600 (to strasznie olbrzymia kategoria).
Struktura HTML wygląda tak:
<div id="list-of-products">
<div class="product product-simple">
<span class="product__title">Produkt 1</span>
<button type="button" data-product-id="89147" class="btn buy-button js-add-to-cart">
Dodaj do koszyka
</button>
</div>
<div class="product product-simple">
<span class="product__title">Produkt 2</span>
<button type="button" data-product-id="89148" class="btn buy-button js-add-to-cart">
Dodaj do koszyka
</button>
</div>
<div class="product product-simple">
<span class="product__title">Produkt 3</span>
<button type="button" data-product-id="89149" class="btn buy-button js-add-to-cart">
Dodaj do koszyka
</button>
</div>
...
<div class="product product-simple">
<span class="product__title">Produkt 16</span>
<button type="button" data-product-id="89156" class="btn buy-button js-add-to-cart">
Dodaj do koszyka
</button>
</div>
</div>
<ul id="pagination">
<li data-page="1" class="active">1</li>
<li data-page="2" class="">2</li>
<li data-page="3" class="">3</li>
...
<li data-page="100" class="">100</li>
</ul>
Zacznijmy od napisania obsługi przycisku add to cart.
window.addEventListener('load', () => {
const addToCartButtons = document.querySelectorAll('.js-add-to-cart')
if (addToCartButtons != null) {
addToCartButtons.forEach(button => {
button.addEventListener("click", (event) => {
const self = event.currentTarget
const productId = self.getAttribute("data-product-id")
if (
typeof productId !== undefined
&& productId != ''
) {
sendRequestToDataBase('add-to-cart', productId);
}
})
});
}
})
const sendRequestToDataBase = (type, productID) => {
// create request, send data to database
}
Jak widzisz, użyłem addEventListener nie tylko na button.js-add-to-cart ale również na elemecie window, ale o tym możesz poczytać tutaj
Powyższy kod działa w taki sposób:
- Po załadowaniu strony (window load) poszukaj elementów posiadających klasę js-add-to-cart.
- Jeżeli takie elementy istnieją to do każdego z nich przypisz funkcję anonimową, wywoływaną w momencie naciśnięcia w przycisk.
- Po naciśnięciu w przycisk, funkcja sprawdza czy element ten posiada atrybut przechowujący ID Produktu,
- jeżeli tak to wywołaj funkcję wysyłającą zapytanie do bazy.
Funkcją sendRequestToDataBase nie będziemy się zajmować w tym wpisie.
Tym oto sposobem, przypisaliśmy łącznie 16 listenerów, do każdego z przycisków posiadających klasę js-add-to-cart.
Teraz dopiszmy obsługę paginacji
window.addEventListener('load', () => {
...
const productsContainer = document.getElementById('#list-of-products')
const paginationElements = document.querySelectorAll('#pagination li')
if (paginationElements != null) {
paginationElements.forEach(element => {
element.addEventListener("click", (event) => {
const self = event.currentTarget
const pageNumber = self.getAttribute("data-page")
if (
typeof pageNumber !== undefined
&& pageNumber != ''
) {
loadProducts(productsContainer, pageNumber);
}
})
});
}
})
const loadProducts = (container, pageNumber) => {
const newListOfProductsHtml = fetchProductsFromDatabaseHTML(pageNumber)
if (newListOfProductsHtml != '') {
container.innerHtml = newListOfProductsHtml
}
}
const fetchingProductsFromDatabaseHTML = (pageNumber) => {
// load new HTML products and return html
}
Tak jak w przypadku dodawania listenerów do przycisków, tym razem również dodajemy Listenera „click”, który przechwytuje interakcję użytkownika z elementami paginacji.
Działanie powyższego skryptu:
- Po załadowaniu strony (window load) poszukaj kontener posiadającego ID list-of-products. Do tego kontenera w późniejszym etapie zostaną dodane produkty
- Następnie odszukaj elementy paginacji
- Jeżeli elementy paginacji istnieją to do każdego z nich przypisz funkcję anonimową, wywoływaną w momencie naciśnięcia w przycisk.
- Po naciśnięciu w przycisk, funkcja sprawdza czy element ten posiada atrybut przechowujący numer strony,
- jeżeli tak to wywołaj funkcję dodającą nową listę produktów.
Funkcja loadProducts przyjmuje 2 argumenty, kontener do którego mają być przypisane nowe produkty oraz numer strony,.
Funkcją fetchingProductsFromDatabaseHTML nie będziemy się zajmować w tym wpisie. Załóżmy, że funkcja ta zwraca kod HTML który zostanie wstrzyknięty do kontenera