Kolejny projekt, w którym korzystami z API. Tym razem dynamicznie tworzymy elementy DOM w oparciu o dane z API i korzystamy z bardziej zaawansowanych rozwiązań.
Templatka startowa plus wytyczne
Zaczynamy z tym kodem:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p id="joke">Loading joke...</p>
<label for="category">Category</label>
<select name="category" id="category">
<option value="all">All</option>
</select>
<button id="jokeBtn">Load joke</button>
<script>
window.addEventListener('DOMContentLoaded', function(){
console.log("DOM fully loaded");
});
</script>
</body>
</html>
Chcemy:
- połączyć się ze znanym już nam Chuck Norris API i zaciągnąć losowy dowcip umieszczając go w tagu <p>
- połączyć się z endpointem Chuck Norris API zwracającym wszystkie możliwe kategorie i w oparciu o dane zwrotne utworzyć elementy <option> w naszym <select>
- dać możliwość użytkownikowi zaciągania kolejnych dowcipów, także z ustawioną przez niego kategorią.
Zaciągnięcie dowcipu na „dzień dobry” – powtórka
Łapiemy nasz paragraf przez querySelector, tworzymy stałą z adresem API, korzystamy z fetch:
window.addEventListener('DOMContentLoaded', function(){
let jokeP = document.querySelector("#joke");
const API_URL = "https://api.chucknorris.io/jokes/random";
fetch(API_URL)
.then(response => response.json())
.then(data => data.value)
.then((joke) => jokeP.textContent = joke);
});
Cały ten „.then” pipe działa w ten sposób:
- wyjmij z odpowiedzi json (czyli pomiń różne nagłówki, statusy odpowiedzi 200, wyjmij mi przesłaną treść odpowedzi) i przekaż dalej
- weź to, co przekazał poprzedni .then, nazwij to data i wyciągnij z tego klucz value (pod tym kluczem znajduje się treść dowcipu)
- weź to, co przekazał poprzedni .then, nazwij to joke i wklej jako zawartość tekstową do elementu jokeP (nasz <p>)
Zaciągnięcie kategorii
Tworzymy stałą dla URLa, z którego pobieramy kategorie:
const API_CAT_URL = "https://api.chucknorris.io/jokes/categories";
Tworzymy szkielet funkcji, która dodaje kategorie do <select>
function createOption(category) {
console.log(category);
}
Teraz zaciągamy kategorie (które przychodzą w postaci listy) z naszego API i wrzucamy w funkcję createOption każdą kategorię z osobna:
fetch(API_CAT_URL)
.then(response => response.json())
.then(data => {
data.forEach((cat) => createOption(cat));
});
Jak widać loguje nam każdą z osobna.
Funkcja createOption – tworzenie elementów DOM
Musimy złapać element <select>, do którego będziemy dodawać opcje:
let select = document.querySelector("#category");
Teraz bierzemy się za tworzenie elementu <option> w funkcji createOption:
function createOption(category) {
let newOption = document.createElement('option');
newOption.value = category;
newOption.textContent = category;
select.appendChild(newOption);
}
Musimy jeszcze gdzieś zapisywać wybraną kategorię:
let select = document.querySelector("#category");
let selectedCat = select.value;
Potrzebna nam jeszcze funkcja, która obsługuje zmianę kategorii:
function changeCategoryHandler() {
selectedCat = select.value;
console.log(selectedCat);
}
Na sam koniec musimy dodać event-listener, który wywołuje funkcję za każdym razem, gdy zmieni się wybrana kategoria:
function changeCategoryHandler() {
selectedCat = select.value;
console.log(selectedCat);
}
select.addEventListener('change', changeCategoryHandler);
W zasadzie działa. Mnie osobiście razi natomiast, że textContent naszych opcji jest pisany małą literą. Kategorie są mają literą (value naszego <option>), ale tekst nie musi być.
Dodajmy tę sprytną funkcję do naszego projektu:
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
Teraz użyjmy jej w odpowiednim miejscu – czyli tam, gdzie dodajemy textContent (value musi być małą literą, inaczej API nie zrozumie, ale użytkownik nie jest API):
function createOption(category) {
let newOption = document.createElement('option');
newOption.value = category;
newOption.textContent = capitalizeFirstLetter(category);
select.appendChild(newOption);
}
Teraz wszystkie nasze kategorie dla użytkownika zapisane są wielką literą, natomiast logowane w konsoli małą, w postaci takiej, jaką API rozumie.
Dodajemy główną funkcjonalność
Teraz, w zależności od tego, czy kategoria jest ustawiona na „all” czy na cokolwiek innego, będziemy mieli różny URL. Na początku złapmy sobie nasz guzik:
let jokeBtn = document.querySelector("#jokeBtn");
Teraz dodajmy event-listener z funkcją wybierania odpowiedniego endpointa:
jokeBtn.addEventListener('click', function(){
const endpoint = selectedCat === 'all' ? API_URL : `${API_URL}?category=${selectedCat}`;
console.log(endpoint);
});
Teraz kategoria ustawiona na all loguje w konsoli:
https://api.chucknorris.io/jokes/random
Kategoria ustawiona na animal loguje nam:
https://api.chucknorris.io/jokes/random?category=animal
Teraz już z górki, jak mamy poprawny endpoint:
jokeBtn.addEventListener('click', function(){
const endpoint = selectedCat === 'all' ? API_URL : `${API_URL}?category=${selectedCat}`;
joke.textContent = "Ładowanie";
fetch(endpoint)
.then(response => response.json())
.then(data => data.value)
.then((joke) => jokeP.textContent = joke);
});
To wszystko. Projekt działa, jak należy.