Aggiungi il trascinamento a un elemento mobile esistente, ad esempio un riquadro, una tavolozza, una barra degli strumenti, un inspector o una finestra di dialogo. Il trascinamento non determina il modo in cui l'elemento viene visualizzato, ignorato, impilato o attivato.
Utilizza un handle dedicato
Inizia a trascinare da un handle dedicato quando l'elemento contiene testo, pulsanti, input o link selezionabili. In questo modo, i contenuti normali rimangono utilizzabili. Rendi l'handle attivabile per lo spostamento da tastiera e assegnagli un nome accessibile che menzioni l'interazione con i tasti Freccia.
Non applicare role="application" alla maniglia. Modifica inutilmente l'interazione con le tecnologie per la disabilità.
Applica cursor: grab/grabbing, touch-action: none e sia user-select: none che -webkit-user-select: none al punto di manipolazione, non all'intero elemento trascinabile, in modo che il testo al di fuori rimanga selezionabile e i gesti di tocco mantengano il loro comportamento abituale:
.drag-handle {
cursor: grab;
touch-action: none;
-webkit-user-select: none;
user-select: none;
}
.drag-handle.dragging {
cursor: grabbing;
}
Preserva la posizione e la raggiungibilità del puntatore
Utilizza un modello di posizionamento relativo all'area visibile, ad esempio position: fixed, quando blocchi il movimento nell'area visibile.
- Avvia un trascinamento solo per il pulsante del puntatore principale (
event.button === 0). - Registra l'offset del puntatore su
pointerdowne utilizzalo per ogni spostamento in modo che l'elemento non salti quando viene afferrato lontano dal suo angolo. - Mantieni attivo il trascinamento dopo che il puntatore lascia l'handle. Imposta il valore predefinito su
setPointerCapture()e cancella lo stato di trascinamento sulostpointercapture. Se l'handle può essere rimosso o sottoposto nuovamente a rendering durante il trascinamento, utilizza i listenerpointermove,pointerupepointercancela livello di documento, poiché in questo caso l'acquisizione viene persa. - Se vuoi, attendi circa 4 px di movimento prima di attivare lo stato di trascinamento, in modo che un clic sulla maniglia non attivi lo stile di trascinamento.
Con l'acquisizione del puntatore, i listener di movimento possono rimanere sul punto di manipolazione:
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`;
}
Blocca ogni posizione del puntatore e della tastiera tramite lo stesso helper in modo che l'elemento rimanga raggiungibile. Blocca nuovamente la posizione corrente dopo le modifiche all'area visibile o alle dimensioni degli elementi. Per gli elementi con un layout pesante, valuta la possibilità di eseguire la traslazione durante il trascinamento per evitare il layout per frame, quindi applica la posizione finale a left e top al rilascio.
Fornisci il movimento della tastiera
Quando il punto di manipolazione è attivo, sposta l'elemento con i tasti Freccia e chiama preventDefault() per questi tasti. Utilizza lo stesso assistente di posizionamento bloccato del movimento del puntatore. Un incremento di 15 px è un punto di partenza ragionevole; adattalo alle dimensioni e alle esigenze di precisione del componente.
Nota specifica per la finestra di dialogo
Un <dialog> o un [popover] può utilizzare questo meccanismo con la sua intestazione come handle. Imposta margin: 0 prima di assegnare left e top, perché i margini predefiniti del browser interferiscono con il posizionamento manuale. Per il comportamento di apertura, chiusura, attivazione e chiusura di finestre di dialogo e popover, consulta Controllo dichiarativo di finestre di dialogo e popover e Chiusura delle finestre di dialogo dei controlli della piattaforma.
Strategie di riserva
Quando il trascinamento serve solo a riposizionare un componente, trattalo come un potenziamento progressivo: senza JavaScript, mantieni l'elemento, i suoi contenuti e i suoi controlli visibili, leggibili e funzionali in una posizione predefinita sensata. Non esporre un handle di trascinamento non funzionale: se l'handle è un controllo di presa autonomo, nascondilo per impostazione predefinita e mostralo quando JavaScript viene inizializzato; se un'intestazione funge anche da handle, collega le sue affordance di trascinamento quando JavaScript viene inizializzato.
Includi -webkit-user-select: none prima di user-select: none in modo che la selezione del testo rimanga disattivata durante il trascinamento in Safari.