Tworzenie elementu, który można przeciągać

Funkcje internetowe: Pointer Events touch-action user-select

Dodaj przeciąganie do istniejącego elementu pływającego, takiego jak panel, paleta, pasek narzędzi, inspektor lub okno dialogowe. Przeciąganie nie określa sposobu wyświetlania, odrzucania, układania ani zaznaczania elementu.

Używanie dedykowanego nicka

Zacznij przeciągać od specjalnego uchwytu, gdy element zawiera tekst, przyciski, dane wejściowe lub linki, które można wybrać. Dzięki temu jego normalna zawartość pozostaje użyteczna. Umożliwiaj zaznaczanie uchwytu za pomocą klawiatury i nadaj mu nazwę na potrzeby ułatwień dostępu, która wspomina o interakcji za pomocą klawiszy strzałek.

Nie nakładaj role="application" na uchwyt. niepotrzebnie zmienia interakcję z technologią wspomagającą osoby z niepełnosprawnością,

Zastosuj cursor: grab/grabbing, touch-action: none oraz user-select: none i -webkit-user-select: none do uchwytu, a nie do całego elementu, który można przeciągać, aby tekst poza nim można było zaznaczać, a gesty dotykowe zachowywały swoje zwykłe działanie:

.drag-handle {
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.drag-handle.dragging {
  cursor: grabbing;
}

Zachowanie pozycji wskaźnika i dostępności

Jeśli chcesz ograniczyć ruch do widocznego obszaru, użyj modelu pozycjonowania względem widocznego obszaru, takiego jak position: fixed.

  • Rozpocznij przeciąganie tylko za pomocą głównego przycisku wskaźnika (event.button === 0).
  • Zapisz przesunięcie wskaźnika na elemencie pointerdown i używaj go przy każdym ruchu, aby element nie przeskakiwał, gdy zostanie chwycony poza rogiem.
  • Utrzymuj przeciąganie po opuszczeniu uchwytu przez wskaźnik. Domyślnie ustaw setPointerCapture() i wyczyść stan przeciągania w lostpointercapture. Jeśli uchwyt może zostać usunięty lub ponownie wyrenderowany w trakcie przeciągania, użyj detektorów pointermove, pointerup i pointercancel na poziomie dokumentu, ponieważ w takim przypadku przechwytywanie zostanie utracone.
  • Opcjonalnie możesz poczekać na ruch o około 4 piksele, zanim przejdziesz do stanu przeciągania, aby kliknięcie uchwytu nie powodowało zmiany stylu przeciągania.

Dzięki przechwytywaniu wskaźnika detektory ruchu mogą pozostać na uchwycie:

handle.addEventListener('pointerdown', (event) => {
  if (event.button !== 0 || event.target.closest('button, input, select, textarea, a')) return;

  const rect = draggable.getBoundingClientRect();
  offsetX = event.clientX - rect.left;
  offsetY = event.clientY - rect.top;
  handle.setPointerCapture(event.pointerId);
});

handle.addEventListener('pointermove', (event) => {
  if (!handle.hasPointerCapture(event.pointerId)) return;
  handle.classList.add('dragging');
  moveTo(event.clientX - offsetX, event.clientY - offsetY);
});

handle.addEventListener('lostpointercapture', () => {
  handle.classList.remove('dragging');
});

function moveTo(left, top) {
  const maxLeft = Math.max(0, window.innerWidth - draggable.offsetWidth);
  const maxTop = Math.max(0, window.innerHeight - draggable.offsetHeight);

  draggable.style.left = `${Math.max(0, Math.min(left, maxLeft))}px`;
  draggable.style.top = `${Math.max(0, Math.min(top, maxTop))}px`;
}

Zablokuj każdą pozycję wskaźnika i klawiatury za pomocą tego samego pomocnika, aby element był dostępny. ponownie przypiąć go w bieżącej pozycji po zmianie rozmiaru widocznego obszaru lub elementu. W przypadku elementów o skomplikowanym układzie rozważ translację podczas przeciągania, aby uniknąć układu dla każdej klatki, a następnie po zwolnieniu zatwierdź ostateczną pozycję we właściwościach left i top.

Zapewnij możliwość poruszania się za pomocą klawiatury

Gdy uchwyt jest zaznaczony, przesuń element za pomocą klawiszy strzałek i wywołaj preventDefault() dla tych klawiszy. Użyj tego samego pomocnika pozycjonowania z ograniczeniem co w przypadku ruchu wskaźnika. Dobrym punktem wyjścia jest przyrost o 15 pikseli. Dostosuj go do rozmiaru komponentu i wymaganej precyzji.

Uwaga dotycząca okna

<dialog> lub [popover] może używać tego mechanizmu z nagłówkiem jako uchwytem. Ustaw margin: 0 przed przypisaniem left i top, ponieważ domyślne marginesy przeglądarki zakłócają ręczne pozycjonowanie. Informacje o zachowaniu okien dialogowych i okienek popover podczas otwierania, zamykania, zaznaczania i odrzucania znajdziesz w artykułach Declarative Dialog and Popover Control (Deklaratywne okno dialogowe i kontrola okienek popover) oraz Platform Controls Dismiss Dialog (Okno dialogowe odrzucania elementów sterujących platformy).

Strategie zastępcze

Jeśli przeciąganie tylko zmienia pozycję komponentu, potraktuj to jako progresywne ulepszanie: bez JavaScriptu element, jego zawartość i elementy sterujące powinny być widoczne, czytelne i funkcjonalne w rozsądnej domyślnej pozycji. Nie udostępniaj niedziałającego uchwytu do przeciągania: jeśli uchwyt jest samodzielnym elementem sterującym, ukryj go domyślnie i wyświetl, gdy JavaScript zostanie zainicjowany. Jeśli nagłówek pełni funkcję uchwytu, dołącz jego funkcje przeciągania, gdy JavaScript zostanie zainicjowany.

Uwzględnij -webkit-user-select: none przed user-select: none, aby zaznaczanie tekstu było wyłączone podczas przeciągania w Safari.