Ajoutez le déplacement à un élément flottant existant, tel qu'un panneau, une palette, une barre d'outils, un inspecteur ou une boîte de dialogue. Le glissement ne détermine pas la façon dont l'élément est affiché, fermé, empilé ou sélectionné.
Utiliser un identifiant dédié
Commencez à faire glisser l'élément à partir d'une poignée dédiée lorsqu'il contient du texte, des boutons, des entrées ou des liens sélectionnables. Cela permet de conserver son contenu normal utilisable. Rendez la poignée sélectionnable pour le déplacement au clavier et donnez-lui un nom accessible qui mentionne l'interaction avec les touches fléchées.
N'appliquez pas role="application" à la poignée. Il modifie inutilement l'interaction avec la technologie d'assistance.
Appliquez cursor: grab/grabbing, touch-action: none, ainsi que user-select: none et -webkit-user-select: none à la poignée, et non à l'ensemble de l'élément déplaçable, afin que le texte en dehors de celle-ci reste sélectionnable et que les gestes tactiles conservent leur comportement habituel :
.drag-handle {
cursor: grab;
touch-action: none;
-webkit-user-select: none;
user-select: none;
}
.drag-handle.dragging {
cursor: grabbing;
}
Conserver la position et l'accessibilité du pointeur
Utilisez un modèle de positionnement relatif à la fenêtre d'affichage, tel que position: fixed, lorsque vous limitez le mouvement à la fenêtre d'affichage.
- Démarre un glissement uniquement pour le bouton du pointeur principal (
event.button === 0). - Enregistrez le décalage du pointeur sur
pointerdownet utilisez-le à chaque déplacement pour que l'élément ne saute pas lorsqu'il est saisi en dehors de son angle. - Maintenir le glissement actif après que le pointeur a quitté la poignée. Définissez la valeur par défaut sur
setPointerCapture()et effacez l'état de déplacement surlostpointercapture. Si la poignée peut être supprimée ou affichée à nouveau pendant le déplacement, utilisez plutôt des écouteurspointermove,pointerupetpointercancelau niveau du document, car la capture est perdue dans ce cas. - Vous pouvez éventuellement attendre un mouvement d'environ 4 px avant d'entrer dans l'état de déplacement, afin qu'un clic sur la poignée ne déclenche pas le style de déplacement.
Avec la capture du pointeur, les écouteurs de mouvement peuvent rester sur la poignée :
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`;
}
Limitez chaque position du pointeur et du clavier à l'aide du même outil d'aide afin que l'élément reste accessible. Réajustez sa position actuelle après avoir modifié la taille de la fenêtre d'affichage ou de l'élément. Pour les éléments lourds en termes de mise en page, envisagez d'effectuer une translation pendant le déplacement pour éviter une mise en page par frame, puis de valider la position finale sur left et top lors du relâchement.
Fournir un mouvement au clavier
Lorsque la poignée est sélectionnée, déplacez l'élément à l'aide des touches fléchées et appelez preventDefault() pour ces touches. Utilisez le même outil d'aide au positionnement limité que pour le mouvement du pointeur. Un incrément de 15 px est un point de départ raisonnable. Adaptez-le à la taille du composant et aux besoins de précision.
Remarque spécifique à la boîte de dialogue
Un <dialog> ou un [popover] peut utiliser ce mécanisme avec son en-tête comme handle. Définissez margin: 0 avant d'attribuer left et top, car les marges par défaut du navigateur interfèrent avec le positionnement manuel. Pour en savoir plus sur le comportement d'ouverture, de fermeture, de sélection et de masquage des boîtes de dialogue et des popovers, consultez Contrôle déclaratif des boîtes de dialogue et des popovers et Masquer la boîte de dialogue des commandes de la plate-forme.
Stratégies de remplacement
Lorsque le glisser-déposer ne sert qu'à repositionner un composant, traitez-le comme une amélioration progressive : sans JavaScript, l'élément, son contenu et ses commandes doivent rester visibles, lisibles et fonctionnels dans une position par défaut logique. N'exposez pas de poignée de déplacement non fonctionnelle : si la poignée est un contrôle de saisie autonome, masquez-la par défaut et affichez-la lorsque JavaScript s'initialise. Si un en-tête sert également de poignée, associez ses affordances de déplacement lorsque JavaScript s'initialise.
Incluez -webkit-user-select: none avant user-select: none pour que la sélection de texte reste supprimée pendant le déplacement dans Safari.