Lekcja 16: Narzędzia Pomocnicze jQuery (Utilities) i Przechowywanie Danych (.data()
)
jQuery dostarcza zestawu użytecznych funkcji pomocniczych (utilities), które nie operują bezpośrednio na elementach DOM, ale ułatwiają wykonywanie typowych zadań programistycznych. Ponadto, oferuje mechanizm .data()
do przechowywania dowolnych danych powiązanych z elementami DOM, bez modyfikowania samych atrybutów HTML.
Narzędzia Pomocnicze (Utilities)
Są to funkcje dostępne bezpośrednio w obiekcie $
(lub jQuery
). Oto kilka najczęściej używanych:
$.each(kolekcja, callback(index, element))
Uniwersalna funkcja do iterowania po obiektach i tablicach. Różni się od metody .each()
wywoływanej na selektorze jQuery.
// Iteracja po tablicy
let kolory = ["czerwony", "zielony", "niebieski"];
$.each(kolory, function(index, kolor) {
console.log(`Index: ${index}, Kolor: ${kolor}`);
});
// Iteracja po obiekcie
let osoba = { imie: "Jan", nazwisko: "Kowalski", wiek: 30 };
$.each(osoba, function(klucz, wartosc) {
console.log(`${klucz}: ${wartosc}`);
});
// Zwrócenie `false` w callbacku przerywa pętlę (jak `break`)
$.each(kolory, function(index, kolor) {
console.log(kolor);
if (kolor === "zielony") {
return false; // Zatrzymaj pętlę
}
});
$.map(kolekcja, callback(element, index))
Tworzy nową tablicę, przekształcając elementy oryginalnej tablicy lub obiektu za pomocą funkcji callback.
let liczby = [1, 2, 3, 4, 5];
let kwadraty = $.map(liczby, function(liczba, index) {
return liczba * liczba;
});
console.log(kwadraty); // Output: [1, 4, 9, 16, 25]
// Zwrócenie `null` lub `undefined` usuwa element z wynikowej tablicy
let parzysteKwadraty = $.map(liczby, function(liczba) {
return (liczba % 2 === 0) ? liczba * liczba : null;
});
console.log(parzysteKwadraty); // Output: [4, 16]
// Można też "spłaszczać" tablice zagnieżdżone
let tablice = [[1, 2], [3, 4], [5]];
let splaszczona = $.map(tablice, function(subTablica) {
return subTablica; // Zwracamy podtablicę, $.map ją rozpakuje
});
console.log(splaszczona); // Output: [1, 2, 3, 4, 5]
$.grep(tablica, callback(element, index), [invert])
Filtruje tablicę, zwracając nową tablicę zawierającą tylko te elementy, dla których funkcja callback zwróciła true
.
let liczby = [1, 2, 3, 4, 5, 6];
let parzyste = $.grep(liczby, function(liczba, index) {
return liczba % 2 === 0;
});
console.log(parzyste); // Output: [2, 4, 6]
// Użycie opcji `invert` (trzeci argument = true) zwraca elementy, dla których callback dał `false`
let nieparzyste = $.grep(liczby, function(liczba) {
return liczba % 2 === 0;
}, true);
console.log(nieparzyste); // Output: [1, 3, 5]
$.extend([deep], target, object1, [objectN])
Łączy zawartość dwóch lub więcej obiektów w pierwszy obiekt (target
). Przydatne do tworzenia domyślnych opcji lub klonowania obiektów.
let domyslneOpcje = { kolor: "niebieski", rozmiar: "średni", widoczny: true };
let uzytkownikaOpcje = { rozmiar: "duży", tlo: "szare" };
// Połącz opcje użytkownika z domyślnymi (modyfikuje domyslneOpcje!)
let finalneOpcje = $.extend(domyslneOpcje, uzytkownikaOpcje);
console.log(finalneOpcje);
// Output: { kolor: "niebieski", rozmiar: "duży", widoczny: true, tlo: "szare" }
console.log(domyslneOpcje === finalneOpcje); // Output: true
// Aby nie modyfikować oryginałów, przekaż pusty obiekt jako pierwszy argument
let domyslne = { a: 1, b: { x: 10 } };
let nowe = { b: { y: 20 }, c: 3 };
let polaczone = $.extend({}, domyslne, nowe);
console.log(polaczone); // Output: { a: 1, b: { y: 20 }, c: 3 } - płytkie łączenie, b.x zniknęło!
console.log(domyslne); // Output: { a: 1, b: { x: 10 } } - oryginał nietknięty
// Głębokie łączenie (rekurencyjne) - pierwszy argument `true`
let glebokoPolaczone = $.extend(true, {}, domyslne, nowe);
console.log(glebokoPolaczone); // Output: { a: 1, b: { x: 10, y: 20 }, c: 3 } - b.x zostało zachowane
Inne Przydatne Narzędzia
$.trim(string)
: Usuwa białe znaki z początku i końca stringa.$.isArray(obj)
: Sprawdza, czy obiekt jest tablicą.$.isFunction(obj)
: Sprawdza, czy obiekt jest funkcją.$.isNumeric(value)
: Sprawdza, czy wartość jest liczbą lub może być na nią przekonwertowana.$.type(obj)
: Zwraca wewnętrzny typ obiektu jako string (np. "string", "number", "array", "object", "function").$.now()
: Zwraca aktualny czas jako liczbę milisekund od epoki (jakDate.now()
).
Przechowywanie Danych za pomocą .data()
Metoda .data()
pozwala na dołączanie i odczytywanie dowolnych danych powiązanych z wybranymi elementami DOM. Dane te są przechowywane wewnętrznie przez jQuery i nie są widoczne jako atrybuty HTML (w przeciwieństwie do atrybutów data-*
).
Składnia:
$(selektor).data(klucz, wartosc)
: Zapisuje dane pod podanym kluczem dla wybranych elementów.$(selektor).data(klucz)
: Odczytuje dane powiązane z pierwszym pasującym elementem dla podanego klucza.$(selektor).data()
: Odczytuje wszystkie dane powiązane z pierwszym pasującym elementem jako obiekt.$(selektor).removeData(klucz)
: Usuwa dane powiązane z podanym kluczem.$(selektor).removeData()
: Usuwa wszystkie dane powiązane z elementami.
// Zapis danych
$("#moj-div").data("uzytkownikId", 123);
$("#moj-div").data("opcje", { kolor: "czerwony", aktywny: true });
// Odczyt danych
let id = $("#moj-div").data("uzytkownikId"); // 123
let opcje = $("#moj-div").data("opcje"); // { kolor: "czerwony", aktywny: true }
let wszystkieDane = $("#moj-div").data(); // { uzytkownikId: 123, opcje: { ... } }
console.log(opcje.kolor); // "czerwony"
// Modyfikacja danych (pobrany obiekt jest referencją)
opcje.aktywny = false;
console.log($("#moj-div").data("opcje").aktywny); // false
// Usuwanie danych
$("#moj-div").removeData("uzytkownikId");
console.log($("#moj-div").data("uzytkownikId")); // undefined
// Odczyt atrybutów data-* (jeśli nie ma danych zapisanych przez .data())
// <div id="inny-div" data-produkt-id="456" data-cena="99.99">...</div>
console.log($("#inny-div").data("produktId")); // 456 (jQuery konwertuje na liczbę)
console.log($("#inny-div").data("cena")); // 99.99 (jQuery konwertuje na liczbę)
console.log($("#inny-div").data()); // { produktId: 456, cena: 99.99 }
// Uwaga: jQuery konwertuje nazwy atrybutów data-* (kebab-case) na camelCase
// <div data-max-wartosc="100">...</div>
console.log($("div").data("maxWartosc")); // 100
Zalety .data()
:
- Można przechowywać dowolne typy danych (liczby, stringi, obiekty, tablice, funkcje), a nie tylko stringi jak w atrybutach
data-*
. - Nie zaśmieca kodu HTML dodatkowymi atrybutami.
- Unika potencjalnych problemów z konwersją typów przy odczycie atrybutów
data-*
.
Zadanie praktyczne
Stwórz listę <ul id="lista-zadan">
z kilkoma elementami <li>
reprezentującymi zadania.
Używając $.each()
i .data()
:
- Przygotuj tablicę obiektów JavaScript reprezentujących zadania, np.:
[{ id: 1, tresc: "Zrobić zakupy", priorytet: "wysoki" }, { id: 2, tresc: "Napisać raport", priorytet: "średni" }]
- Użyj
$.each()
, aby przeiterować po tej tablicy. - Dla każdego zadania, stwórz nowy element
<li>
z treścią zadania. - Użyj
.data()
, aby zapisaćid
ipriorytet
zadania w danych powiązanych z nowo utworzonym elementem<li>
. - Dodaj nowo utworzone
<li>
do listy<ul id="lista-zadan">
. - Dodaj obsługę kliknięcia na elementy
<li>
. Po kliknięciu, odczytajid
ipriorytet
z danych klikniętego elementu za pomocą.data()
i wyświetl je w konsoli.
Pokaż rozwiązanie
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Test $.each i .data()</title>
<style>
#lista-zadan li { cursor: pointer; margin: 5px 0; }
#lista-zadan li:hover { background-color: #eee; }
</style>
</head>
<body>
<h2>Lista Zadań</h2>
<ul id="lista-zadan">
<!-- Zadania zostaną dodane tutaj -->
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
$(function() {
let zadania = [
{ id: 1, tresc: "Zrobić zakupy", priorytet: "wysoki" },
{ id: 2, tresc: "Napisać raport", priorytet: "średni" },
{ id: 3, tresc: "Zadzwonić do klienta", priorytet: "wysoki" },
{ id: 4, tresc: "Umyć samochód", priorytet: "niski" }
];
let $lista = $("#lista-zadan");
// Użyj $.each do iteracji i tworzenia elementów listy
$.each(zadania, function(index, zadanie) {
let $noweLi = $("").text(zadanie.tresc);
// Zapisz dane za pomocą .data()
$noweLi.data("zadanieId", zadanie.id);
$noweLi.data("priorytetZadania", zadanie.priorytet);
// Można też zapisać cały obiekt:
// $noweLi.data("pelneZadanie", zadanie);
$lista.append($noweLi);
});
// Obsługa kliknięcia na elementy listy
// Używamy delegowania zdarzeń dla wydajności
$lista.on("click", "li", function() {
let $kliknieteLi = $(this);
// Odczytaj dane
let id = $kliknieteLi.data("zadanieId");
let priorytet = $kliknieteLi.data("priorytetZadania");
// let caleZadanie = $kliknieteLi.data("pelneZadanie");
console.log(`Kliknięto zadanie o ID: ${id}, Priorytet: ${priorytet}`);
// console.log("Cały obiekt zadania:", caleZadanie);
});
});
</script>
</body>
</html>
Zadanie do samodzielnego wykonania
Stwórz prosty formularz z kilkoma polami (np. imię, email). Dodaj przycisk "Zapisz Ustawienia".
Używając $.extend()
i .data()
:
- Zdefiniuj obiekt
domyslneUstawienia
z kilkoma domyślnymi wartościami (np.{ motyw: 'jasny', powiadomienia: true }
). - Po kliknięciu przycisku "Zapisz Ustawienia":
- Stwórz obiekt
biezaceUstawienia
, pobierając wartości z pól formularza. - Użyj
$.extend()
, aby połączyćdomyslneUstawienia
zbiezaceUstawienia
w nowy obiektfinalneUstawienia
(nie modyfikuj obiektu domyślnego). - Zapisz obiekt
finalneUstawienia
w danych elementu<body>
za pomocą.data("ustawieniaUzytkownika", finalneUstawienia)
. - Dodaj drugi przycisk "Pokaż Ustawienia". Po jego kliknięciu, odczytaj zapisane ustawienia z elementu
<body>
i wyświetl je (np. w konsoli lub w osobnym div-ie).
FAQ - Narzędzia Pomocnicze i .data()
w jQuery
Jaka jest różnica między $.each()
a $(selektor).each()
?
$.each()
to ogólna funkcja do iterowania po dowolnych tablicach lub obiektach JavaScript. $(selektor).each()
to metoda wywoływana na obiekcie jQuery (wyniku selektora), służąca do iterowania po znalezionych elementach DOM. W callbacku $(selektor).each()
, this
odnosi się do bieżącego elementu DOM.
Czy .data()
jest tym samym co atrybuty data-*
HTML5?
Nie do końca. jQuery .data()
najpierw sprawdza, czy dane zostały zapisane wewnętrznie za pomocą .data(klucz, wartosc)
. Jeśli nie, próbuje odczytać wartość z odpowiedniego atrybutu data-*
(konwertując nazwę klucza z camelCase na kebab-case). Zapis za pomocą .data()
nie modyfikuje atrybutów HTML, a odczyt atrybutów data-*
przez .data()
wykonuje konwersję typów (np. string "123" staje się liczbą 123).
Czy dane zapisane przez .data()
są usuwane po usunięciu elementu z DOM?
Tak. Gdy element jest usuwany z DOM za pomocą metod jQuery takich jak .remove()
czy .empty()
(które wewnętrznie mogą usuwać elementy potomne), jQuery automatycznie czyści dane powiązane z tymi elementami, aby zapobiec wyciekom pamięci.
Do czego przydaje się głębokie kopiowanie (deep copy) w $.extend()
?
Głębokie kopiowanie ($.extend(true, ...)
) jest potrzebne, gdy łączone obiekty zawierają inne obiekty lub tablice. Bez głębokiego kopiowania, wewnętrzne obiekty/tablice są kopiowane przez referencję, co oznacza, że modyfikacja kopii wpłynie na oryginał (i odwrotnie). Głębokie kopiowanie tworzy niezależne kopie również dla zagnieżdżonych struktur.
Czy istnieją nowsze, natywne odpowiedniki narzędzi jQuery w czystym JavaScript?
Tak, wiele narzędzi jQuery ma swoje odpowiedniki w nowoczesnym JavaScript (ES6+). Na przykład: $.each
-> Array.prototype.forEach
, Object.keys().forEach
; $.map
-> Array.prototype.map
; $.grep
-> Array.prototype.filter
; $.extend
-> Object.assign
(płytkie) lub operator spread {...obj1, ...obj2}
(płytkie); $.trim
-> String.prototype.trim
; $.isArray
-> Array.isArray
.