Lekcja 19: jQuery UI - Interakcje (Draggable, Droppable, Resizable, Sortable)
Oprócz widgetów, jQuery UI oferuje zestaw potężnych interakcji, które pozwalają na dodanie dynamicznego zachowania do elementów DOM, takich jak przeciąganie, upuszczanie, zmiana rozmiaru czy sortowanie. Są one często podstawą do budowy bardziej złożonych interfejsów użytkownika.
Podstawowe Interakcje
Draggable (Przeciąganie)
Umożliwia użytkownikowi przesuwanie elementu po stronie za pomocą myszy.
<div id="element-do-przeciagania" style="width: 100px; height: 100px; background: lightblue; border: 1px solid blue; cursor: move;">
Przeciągnij mnie!
</div>
<script>
$(function() {
$("#element-do-przeciagania").draggable({
axis: "x", // Ogranicz ruch tylko do osi X (możliwe też "y")
containment: "parent", // Ogranicz ruch do elementu nadrzędnego
cursor: "grabbing", // Zmień kursor podczas przeciągania
handle: "p", // Pozwól na przeciąganie tylko za pomocą elementu wewnątrz
opacity: 0.7, // Zmniejsz przezroczystość podczas przeciągania
revert: true, // Element wróci na swoje miejsce po upuszczeniu (może być "invalid" lub "valid")
snap: true, // Przyciągaj do krawędzi innych elementów
// Zdarzenia: start, drag, stop
start: function(event, ui) {
console.log("Rozpoczęto przeciąganie");
},
stop: function(event, ui) {
console.log("Zakończono przeciąganie na pozycji:", ui.position.left, ui.position.top);
}
});
});
</script>
Droppable (Upuszczanie)
Pozwala elementowi reagować na upuszczenie na niego innego elementu (typu Draggable).
<div id="cel-upuszczenia" style="width: 150px; height: 150px; background: lightyellow; border: 1px solid orange;">
Upuść tutaj
</div>
<script>
$(function() {
$("#cel-upuszczenia").droppable({
accept: "#element-do-przeciagania", // Akceptuj tylko elementy pasujące do tego selektora
activeClass: "ui-state-highlight", // Klasa dodawana, gdy kompatybilny element jest przeciągany
hoverClass: "ui-state-active", // Klasa dodawana, gdy kompatybilny element jest nad celem
tolerance: "fit", // Kiedy uznać, że element jest nad celem ("intersect", "pointer", "touch")
// Zdarzenia: activate, deactivate, over, out, drop
drop: function(event, ui) {
$(this).css("background-color", "lightgreen").text("Upuszczono!");
// ui.draggable odnosi się do przeciągniętego elementu jQuery
ui.draggable.hide();
},
over: function(event, ui) {
console.log("Element nad celem");
}
});
});
</script>
Resizable (Zmiana Rozmiaru)
Umożliwia użytkownikowi zmianę rozmiaru elementu poprzez przeciąganie jego krawędzi lub rogów.
<div id="element-do-zmiany-rozmiaru" style="width: 150px; height: 100px; background: lightcoral; border: 1px solid red; overflow: hidden; position: relative;">
Zmień mój rozmiar!
<!-- Uchwyty do zmiany rozmiaru są dodawane automatycznie przez jQuery UI -->
</div>
<script>
$(function() {
$("#element-do-zmiany-rozmiaru").resizable({
alsoResize: "#inny-element", // Zmieniaj rozmiar innego elementu jednocześnie
animate: true, // Animuj zmianę rozmiaru
aspectRatio: true, // Zachowaj proporcje
containment: "parent", // Ogranicz zmianę rozmiaru do rodzica
ghost: true, // Pokazuj "ducha" podczas zmiany rozmiaru
handles: "n, e, s, w, ne, se, sw, nw", // Które uchwyty pokazać (domyślnie wszystkie oprócz "n, e, s, w")
maxHeight: 300,
maxWidth: 400,
minHeight: 50,
minWidth: 80,
// Zdarzenia: create, start, resize, stop
stop: function(event, ui) {
console.log("Nowy rozmiar:", ui.size.width, "x", ui.size.height);
}
});
});
</script>
Sortable (Sortowanie)
Pozwala na zmianę kolejności elementów na liście (lub w innym kontenerze) poprzez przeciąganie.
<ul id="lista-sortowalna" style="list-style-type: none; margin: 0; padding: 0; width: 60%;">
<li class="ui-state-default" style="margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; cursor: move;">Element 1</li>
<li class="ui-state-default" style="margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; cursor: move;">Element 2</li>
<li class="ui-state-default" style="margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; cursor: move;">Element 3</li>
<li class="ui-state-default" style="margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; cursor: move;">Element 4</li>
</ul>
<script>
$(function() {
$("#lista-sortowalna").sortable({
axis: "y", // Ogranicz sortowanie do osi Y
containment: "parent",
cursor: "move",
items: "li", // Które elementy wewnątrz kontenera są sortowalne
opacity: 0.6,
placeholder: "ui-state-highlight", // Klasa dla miejsca, gdzie element zostanie upuszczony
revert: true, // Animacja powrotu elementu
// Zdarzenia: create, start, sort, change, beforeStop, stop, update
update: function(event, ui) {
// Wywoływane po zakończeniu sortowania i zmianie pozycji w DOM
let kolejnosc = $(this).sortable("toArray"); // Pobierz tablicę ID elementów w nowej kolejności
console.log("Nowa kolejność (wymaga nadania ID elementom li):", kolejnosc);
}
});
// $("#lista-sortowalna").disableSelection(); // Zapobiega zaznaczaniu tekstu podczas przeciągania
});
</script>
Łączenie Interakcji
Interakcje można łączyć, np. tworząc listy, między którymi można przeciągać elementy (łączenie Sortable z opcją connectWith
).
<ul id="lista1" class="polaczone-listy">
<li class="ui-state-default">Element A1</li>
<li class="ui-state-default">Element A2</li>
</ul>
<ul id="lista2" class="polaczone-listy">
<li class="ui-state-highlight">Element B1</li>
<li class="ui-state-highlight">Element B2</li>
</ul>
<style>
.polaczone-listy { border: 1px solid #ccc; width: 150px; min-height: 40px; list-style-type: none; margin: 10px; padding: 5px; float: left; }
.polaczone-listy li { margin: 5px; padding: 5px; cursor: move; }
</style>
<script>
$(function() {
$("#lista1, #lista2").sortable({
connectWith: ".polaczone-listy", // Selektor list, z którymi można wymieniać elementy
placeholder: "ui-state-highlight",
revert: true
}).disableSelection();
});
</script>
Zadanie praktyczne
Stwórz prosty interfejs, gdzie użytkownik może przeciągnąć obrazek (<img>
) do "kosza" (<div>
), a po upuszczeniu obrazek znika z animacją, a kosz zmienia wygląd.
- Dodaj element
<img>
z ID "obrazek" i<div>
z ID "kosz". Nadaj im odpowiednie style. - Dołącz jQuery i jQuery UI (Draggable, Droppable, Effects - jeśli chcesz użyć efektów UI do ukrywania).
- Zainicjuj
.draggable()
na obrazku. Ustaw opcjęrevert: "invalid"
. - Zainicjuj
.droppable()
na koszu. Ustawaccept: "#obrazek"
. - W handlerze zdarzenia
drop
dla kosza: - Zmień tło lub dodaj klasę do kosza, aby zasygnalizować upuszczenie.
- Użyj
ui.draggable.hide("explode", 500);
(lub inny efekt jQuery UI, np. "puff", lub proste.fadeOut()
), aby ukryć obrazek z animacją. - Opcjonalnie: W handlerze
over
dla kosza zmień jego wygląd (np. obramowanie), a w handlerzeout
przywróć poprzedni wygląd.
Pokaż rozwiązanie
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Przeciągnij do Kosza</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<style>
#obrazek { width: 80px; height: 80px; cursor: move; border: 1px solid gray; display: block; margin-bottom: 20px; }
#kosz { width: 120px; height: 120px; border: 2px dashed #ccc; background-color: #f8f8f8; text-align: center; line-height: 110px; color: #999; transition: all 0.3s ease; }
#kosz.nad { border-color: orange; background-color: #fffacd; }
#kosz.upuszczono { border-style: solid; border-color: green; background-color: #e9fce9; color: green; }
</style>
</head>
<body>
<h2>Przeciągnij obrazek do kosza</h2>
<img id="obrazek" src="https://via.placeholder.com/80" alt="Obrazek">
<div id="kosz">
Kosz
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- Dołącz jQuery UI (w tym efekty, jeśli używasz np. explode) -->
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<script>
$(function() {
$("#obrazek").draggable({
revert: "invalid"
});
$("#kosz").droppable({
accept: "#obrazek",
classes: {
"ui-droppable-hover": "nad" // Użyj klasy CSS zamiast hoverClass dla płynniejszej zmiany
},
/* Można też użyć zdarzeń over/out:
over: function(event, ui) {
$(this).addClass("nad");
},
out: function(event, ui) {
$(this).removeClass("nad");
},
*/
drop: function(event, ui) {
$(this).removeClass("nad").addClass("upuszczono").html("Usunięto!");
// Ukryj obrazek z efektem "explode" (wymaga dołączenia komponentu Effects z jQuery UI)
// lub użyj prostszego efektu, np. fadeOut
ui.draggable.hide("explode", { pieces: 16 }, 500);
// ui.draggable.fadeOut(500);
}
});
});
</script>
</body>
</html>
Zadanie do samodzielnego wykonania
Stwórz listę zadań (<ul>
z elementami <li>
), którą można sortować za pomocą jQuery UI Sortable.
- Stwórz listę HTML z kilkoma elementami
<li>
reprezentującymi zadania. - Dołącz jQuery i jQuery UI (Sortable).
- Zainicjuj
.sortable()
na elemencie<ul>
. - Dodaj stylizację (np. z klas
ui-state-default
) dla elementów listy, aby wyglądały jak elementy do przeciągania. - Dodaj placeholder (np. klasę
ui-state-highlight
) dla miejsca, gdzie element zostanie upuszczony. - Opcjonalnie: W zdarzeniu
update
pobierz nową kolejność elementów (np. ich ID, jeśli je nadasz) i wyświetl ją w konsoli.
FAQ - Interakcje jQuery UI
Jaka jest różnica między widgetem a interakcją w jQuery UI?
Widgety (np. Datepicker, Dialog) to gotowe komponenty interfejsu z własnym wyglądem i złożoną funkcjonalnością. Interakcje (np. Draggable, Sortable) dodają określone zachowania (przeciąganie, zmiana rozmiaru, sortowanie) do istniejących elementów HTML, dając większą elastyczność w budowie niestandardowych interfejsów.
Czy mogę ograniczyć obszar przeciągania (Draggable)?
Tak, służy do tego opcja containment
. Można podać selektor elementu nadrzędnego (np. "parent"
, "#kontener"
), dokument ("document"
), okno przeglądarki ("window"
) lub tablicę współrzędnych [x1, y1, x2, y2]
definiującą prostokątny obszar.
Jak sprawić, by element Draggable był akceptowany tylko przez niektóre elementy Droppable?
Użyj opcji accept
w konfiguracji .droppable()
. Można tam podać selektor CSS, który musi pasować do przeciąganego elementu (ui.draggable
), aby został on zaakceptowany. Można również podać funkcję, która zwróci true
, jeśli element ma być zaakceptowany.
Czy Sortable działa tylko na listach <ul>
/<ol>
?
Nie, Sortable może działać na dowolnym kontenerze (np. <div>
), a sortowalne elementy mogą być dowolnymi elementami potomnymi (np. inne <div>
). Ważne jest, aby w opcji items
podać odpowiedni selektor dla sortowalnych elementów (domyślnie > *
, czyli wszystkie bezpośrednie dzieci).
Jak zapisać nową kolejność elementów po sortowaniu (Sortable)?
W handlerze zdarzenia update
(lub stop
) można użyć metody .sortable("toArray", { attribute: "id" })
. Zwraca ona tablicę wartości atrybutu (domyślnie id
) sortowanych elementów w nowej kolejności. Tę tablicę można następnie wysłać do serwera za pomocą AJAX, aby zapisać zmiany.