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

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:

// 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():

Zadanie praktyczne

Stwórz listę <ul id="lista-zadan"> z kilkoma elementami <li> reprezentującymi zadania.

Używając $.each() i .data():

  1. Przygotuj tablicę obiektów JavaScript reprezentujących zadania, np.: [{ id: 1, tresc: "Zrobić zakupy", priorytet: "wysoki" }, { id: 2, tresc: "Napisać raport", priorytet: "średni" }]
  2. Użyj $.each(), aby przeiterować po tej tablicy.
  3. Dla każdego zadania, stwórz nowy element <li> z treścią zadania.
  4. Użyj .data(), aby zapisać id i priorytet zadania w danych powiązanych z nowo utworzonym elementem <li>.
  5. Dodaj nowo utworzone <li> do listy <ul id="lista-zadan">.
  6. Dodaj obsługę kliknięcia na elementy <li>. Po kliknięciu, odczytaj id i priorytet 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():

    1. Zdefiniuj obiekt domyslneUstawienia z kilkoma domyślnymi wartościami (np. { motyw: 'jasny', powiadomienia: true }).
    2. Po kliknięciu przycisku "Zapisz Ustawienia":
    3. Stwórz obiekt biezaceUstawienia, pobierając wartości z pól formularza.
    4. Użyj $.extend(), aby połączyć domyslneUstawienia z biezaceUstawienia w nowy obiekt finalneUstawienia (nie modyfikuj obiektu domyślnego).
    5. Zapisz obiekt finalneUstawienia w danych elementu <body> za pomocą .data("ustawieniaUzytkownika", finalneUstawienia).
    6. 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.