Intégration web angularforall.com

- Drag & Drop natif HTML5 : guide complet

Drag Drop Html5 Api Javascript Ux Interactivité
Drag & Drop natif HTML5 : guide complet

Maîtrisez le Drag & Drop natif HTML5 : API Drag & Drop, événements, fichiers, DataTransfer. 13+ exemples prêts à l'emploi sans jQuery.

Introduction : Drag & Drop natif

L'API Drag & Drop HTML5 permet :

  • Glisser des éléments DOM et les déposer ailleurs
  • Upload de fichiers par glisser-déposer (drag-and-drop zones)
  • Partager des données entre les éléments
  • Réorganiser des contenus de manière intuitive
Avantage : Zéro dépendance externe. L'API est native et supportée par tous les navigateurs modernes.

Les événements Drag & Drop

Cycle complet d'une action Drag & Drop

// 1) dragstart : utilisateur commence à glisser
element.addEventListener('dragstart', (e) => {
    e.dataTransfer.effectAllowed = 'copy'; // ou 'move', 'link'
    e.dataTransfer.setData('text/plain', 'data');
});

// 2) dragenter : pointeur entre dans une zone
dropZone.addEventListener('dragenter', (e) => {
    dropZone.classList.add('drag-over');
});

// 3) dragover : pointeur se déplace dans la zone
dropZone.addEventListener('dragover', (e) => {
    e.preventDefault(); // Nécessaire pour activer drop
    e.dataTransfer.dropEffect = 'copy';
});

// 4) dragleave : pointeur sort de la zone
dropZone.addEventListener('dragleave', (e) => {
    dropZone.classList.remove('drag-over');
});

// 5) drop : utilisateur relâche dans la zone
dropZone.addEventListener('drop', (e) => {
    e.preventDefault();
    const data = e.dataTransfer.getData('text/plain');
    console.log('Données reçues :', data);
});

// 6) dragend : drag terminé (sur l'élément original)
element.addEventListener('dragend', (e) => {
    console.log('Drag terminé');
});

Points clés :

  • preventDefault() sur dragover et drop est obligatoire
  • dragstart et dragend se déclenchent sur l'élément glissable
  • dragenter, dragover, dragleave, drop sur la zone de dépôt

Rendre des éléments glissables

Attribut draggable

<!-- Élément glissable -->
<div draggable="true" id="draggable" class="card p-3">
    <h3>Glisse-moi !</h3>
</div>

JavaScript pour supporter le drag

const draggable = document.getElementById('draggable');

draggable.addEventListener('dragstart', (e) => {
    // Passer l'ID en tant que donnée
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/html', draggable.innerHTML);
    e.dataTransfer.setData('text/plain', draggable.id);

    // Optionnel : personnaliser l'image du curseur
    const dragImage = new Image();
    dragImage.src = 'drag-icon.png';
    e.dataTransfer.setDragImage(dragImage, 0, 0);

    draggable.style.opacity = '0.5';
});

draggable.addEventListener('dragend', () => {
    draggable.style.opacity = '1';
});

Créer des zones de dépôt

Zone de dépôt simple

<!-- Zone de dépôt -->
<div id="dropZone" class="drop-zone border-2 border-dashed p-4">
    Dépose un élément ici
</div>

<style>
.drop-zone {
    transition: background-color 0.3s ease;
}
.drop-zone.drag-over {
    background-color: lightblue;
    border-color: blue;
}
</style>

Gestion des événements de la zone

const dropZone = document.getElementById('dropZone');

// Éviter le comportement par défaut du navigateur
dropZone.addEventListener('dragenter', (e) => {
    e.preventDefault();
    dropZone.classList.add('drag-over');
});

dropZone.addEventListener('dragover', (e) => {
    e.preventDefault(); // OBLIGATOIRE
    e.dataTransfer.dropEffect = 'copy';
});

dropZone.addEventListener('dragleave', () => {
    dropZone.classList.remove('drag-over');
});

dropZone.addEventListener('drop', (e) => {
    e.preventDefault();
    dropZone.classList.remove('drag-over');

    const data = e.dataTransfer.getData('text/plain');
    console.log('Élément reçu :', data);

    // Déplacer l'élément
    const element = document.getElementById(data);
    dropZone.appendChild(element);
});

DataTransfer : passer des données

Différents types de données

element.addEventListener('dragstart', (e) => {
    // Texte
    e.dataTransfer.setData('text/plain', 'Texte simple');

    // HTML
    e.dataTransfer.setData('text/html', '<b>Texte en gras</b>');

    // URL
    e.dataTransfer.setData('text/uri-list', 'https://example.com');

    // Custom JSON
    e.dataTransfer.setData('application/json', JSON.stringify({
        id: 123,
        name: 'Mon élément'
    }));
});

dropZone.addEventListener('drop', (e) => {
    e.preventDefault();

    // Récupérer les données
    const plainText = e.dataTransfer.getData('text/plain');
    const html = e.dataTransfer.getData('text/html');
    const url = e.dataTransfer.getData('text/uri-list');
    const json = JSON.parse(e.dataTransfer.getData('application/json'));

    console.log({ plainText, html, url, json });
});

effectAllowed et dropEffect

// Sur dragstart : indiquer le type d'opération
element.addEventListener('dragstart', (e) => {
    e.dataTransfer.effectAllowed = 'copy';  // 'copy', 'move', 'link', 'none'
});

// Sur dragover : indiquer l'effet visuel
dropZone.addEventListener('dragover', (e) => {
    e.dataTransfer.dropEffect = 'copy';  // Affiche le curseur approprié
});

Upload de fichiers avec Drag & Drop

Zone de upload

<div id="uploadZone" class="drop-zone p-5 border-2 border-dashed text-center">
    <p>Glisse des fichiers ici ou <a href="#">clique pour sélectionner</a></p>
    <input type="file" id="fileInput" multiple hidden/>
</div>

Traitement des fichiers

const uploadZone = document.getElementById('uploadZone');
const fileInput = document.getElementById('fileInput');

// Gestion du drop
uploadZone.addEventListener('drop', (e) => {
    e.preventDefault();
    const files = e.dataTransfer.files;
    handleFiles(files);
});

// Gestion du clic
uploadZone.addEventListener('click', () => {
    fileInput.click();
});

fileInput.addEventListener('change', (e) => {
    handleFiles(e.target.files);
});

// Traitement des fichiers
function handleFiles(files) {
    for (let file of files) {
        console.log(`Fichier : ${file.name} (${file.size} bytes)`);

        // Vérifier le type
        if (file.type.startsWith('image/')) {
            // C'est une image
            const reader = new FileReader();
            reader.onload = (e) => {
                const img = document.createElement('img');
                img.src = e.target.result;
                img.style.maxWidth = '200px';
                uploadZone.appendChild(img);
            };
            reader.readAsDataURL(file);
        } else {
            console.log('Fichier non-image :', file.type);
        }
    }
}

// Prévenir le drop par défaut sur tout le document
document.addEventListener('dragover', (e) => e.preventDefault());
document.addEventListener('drop', (e) => e.preventDefault());

Réorganiser des listes

Liste triable

<ul id="sortableList" class="list-group">
    <li draggable="true" class="list-group-item">Élément 1</li>
    <li draggable="true" class="list-group-item">Élément 2</li>
    <li draggable="true" class="list-group-item">Élément 3</li>
</ul>

JavaScript pour trier

const list = document.getElementById('sortableList');
let draggedElement = null;

const items = list.querySelectorAll('li');

items.forEach(item => {
    item.addEventListener('dragstart', () => {
        draggedElement = item;
        item.style.opacity = '0.5';
    });

    item.addEventListener('dragend', () => {
        item.style.opacity = '1';
    });

    item.addEventListener('dragover', (e) => {
        e.preventDefault();
    });

    item.addEventListener('drop', (e) => {
        e.preventDefault();

        if (draggedElement && draggedElement !== item) {
            // Insérer avant si on est dans la moitié supérieure
            list.insertBefore(draggedElement, item);
        }
    });
});

Retours visuels et UX

Feedback visuel pendant le drag

/* Style du curseur */
[draggable="true"] {
    cursor: grab;
    transition: opacity 0.2s ease;
}

[draggable="true"]:active {
    cursor: grabbing;
    opacity: 0.7;
}

/* Zone de dépôt active */
.drop-zone {
    transition: all 0.3s ease;
    border: 2px solid transparent;
}

.drop-zone.drag-over {
    border-color: #007bff;
    background-color: rgba(0, 123, 255, 0.1);
    box-shadow: 0 0 20px rgba(0, 123, 255, 0.2);
}

/* Animation de dépôt réussi */
.drop-success {
    animation: dropSuccess 0.6s ease;
}

@keyframes dropSuccess {
    0% {
        background-color: lightgreen;
    }
    100% {
        background-color: white;
    }
}

Animation de feedback

dropZone.addEventListener('drop', (e) => {
    e.preventDefault();

    // Animation de feedback
    dropZone.classList.add('drop-success');
    setTimeout(() => {
        dropZone.classList.remove('drop-success');
    }, 600);
});

Conclusion et librairies

Drag & Drop maîtrisé : Vous pouvez implémenter des interfaces intuitives et interactives avec l'API native.

Quand utiliser des librairies :

Mais pour 90% des cas simples, l'API native suffit amplement !

Partager