API Drag'n'Drop: exemple d'utilisation

Bonne journée, mes amis!



Dans ce didacticiel, nous examinerons le mécanisme de glisser-déposer intégré sur la page.



Par souci d'Ă©quitĂ©, il convient de noter que ce mĂ©canisme peut ĂȘtre implĂ©mentĂ© en utilisant des Ă©vĂ©nements de souris, comme Ilya Kantor le montre dans son manuel , cependant, nous utiliserons des outils natifs basĂ©s sur la spĂ©cification .



Support technologique:







Aperçu:







Notre tĂąche est la suivante: mettre en Ɠuvre une liste de tĂąches, composĂ©e de trois colonnes: toutes les tĂąches, les tĂąches en cours, les tĂąches terminĂ©es. Bien entendu, l'application doit offrir la possibilitĂ© d'ajouter et de supprimer des tĂąches. En outre, la possibilitĂ© d'un arrangement arbitraire des tĂąches devrait ĂȘtre prĂ©vue. C'est l'une des parties les plus intĂ©ressantes du didacticiel - garder une trace de l'Ă©lĂ©ment sous l'Ă©lĂ©ment glissĂ© et dĂ©terminer oĂč l'Ă©lĂ©ment dĂ©placĂ© doit ĂȘtre positionnĂ© au-dessus ou en dessous de l'Ă©lĂ©ment suivi. Bootstrap



sera utilisĂ© pour le style . Si vous ĂȘtes intĂ©ressĂ©, suivez-moi.







Balisage:



<head>
    <!-- Bootstrap CSS -->
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
      integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
      crossorigin="anonymous"
    />
    <!-- custom CSS -->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body class="container">
    <h1>Drag & Drop Example</h1>
    <main class="row">
      <div class="input-group">
        <div class="input-group-prepend">
          <span class="input-group-text">Enter new todo: </span>
        </div>
        <input
          type="text"
          class="form-control"
          placeholder="todo4"
          data-name="todo-input"
        />
        <div class="input-group-append">
          <button class="btn btn-success" data-name="add-btn">Add</button>
        </div>
      </div>

      <div class="col-4">
        <h3>Todos</h3>
        <ul class="list-group" data-name="todos-list">
          <li class="list-group-item" data-id="1" draggable="true">
            <p>todo1</p>
            <button
              class="btn btn-outline-danger btn-sm"
              data-name="remove-btn"
            >
              X
            </button>
          </li>
          <li class="list-group-item" data-id="2" draggable="true">
            <p>todo2</p>
            <button
              class="btn btn-outline-danger btn-sm"
              data-name="remove-btn"
            >
              X
            </button>
          </li>
          <li class="list-group-item" data-id="3" draggable="true">
            <p>todo3</p>
            <button
              class="btn btn-outline-danger btn-sm"
              data-name="remove-btn"
            >
              X
            </button>
          </li>
        </ul>
      </div>

      <div class="col-4">
        <h3>In Progress</h3>
        <ul class="list-group" data-name="in-progress-list"></ul>
      </div>

      <div class="col-4">
        <h3>Completed</h3>
        <ul class="list-group" data-name="completed-list"></ul>
      </div>
    </main>

    <!-- custom JS -->
    <script src="script.js"></script>
</body>


Ici, nous avons un conteneur avec un champ pour saisir le texte d'une tùche et un bouton pour l'ajouter à la liste (input-group), ainsi que trois conteneurs-colonnes (list-group) pour toutes les tùches (todos-list), les tùches en cours (en -progress-list) et les tùches terminées (liste-terminée). Quant aux attributs "data", ils sont destinés à séparer le style et le contrÎle: classes - pour le style, les données - pour la gestion.



Modes:



body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #222;
}

main {
  max-width: 600px;
}

.input-group {
  margin: 1rem;
}

.list-group {
  min-height: 100px;
  height: 100%;
}

.list-group-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

div + div {
  border-right: 1px dotted #222;
}

h3 {
  text-align: center;
}

p {
  margin: 0;
}

.completed p {
  text-decoration: line-through;
}

.in-progress p {
  border-bottom: 1px dashed #222;
}

.drop {
  background: linear-gradient(#eee, transparent);
  border-radius: 4px;
}



Les classes "en cours" et "terminé" servent d'indicateurs que la tùche est dans la colonne correspondante. La classe «drop» est conçue pour visualiser la tùche qui atteint la zone de dépÎt.



Avant de passer au script, notez que nous n'utiliserons pas tous les événements de glisser-déposer, mais la plupart des principaux.



Nous définissons le conteneur principal dans lequel la recherche d'éléments sera effectuée et auquel le traitement événementiel sera délégué:



const main = document.querySelector("main");


Nous implémentons l'ajout et la suppression de tùches en traitant un clic:



main.addEventListener("click", (e) => {
  //     
  if (e.target.tagName === "BUTTON") {
    //      "data-name"
    const { name } = e.target.dataset;
    //         
    if (name === "add-btn") {
      //      
      const todoInput = main.querySelector('[data-name="todo-input"]');
      //     
      if (todoInput.value.trim() !== "") {
        //   
        const value = todoInput.value;
        //   
        const template = `
        <li class="list-group-item" draggable="true" data-id="${Date.now()}">
          <p>${value}</p>
          <button class="btn btn-outline-danger btn-sm" data-name="remove-btn">X</button>
        </li>
        `;
        //   
        const todosList = main.querySelector('[data-name="todos-list"]');
        //     
        todosList.insertAdjacentHTML("beforeend", template);
        //      
        todoInput.value = "";
      }
    //       
    } else if (name === "remove-btn") {
      //   
      e.target.parentElement.remove();
    }
  }
});


Passons directement au glisser.



Pour commencer, nous implémentons d'entrer dans la zone "lancer" et de la quitter en ajoutant / supprimant la classe appropriée:



main.addEventListener("dragenter", (e) => {
  //    
  if (e.target.classList.contains("list-group")) {
    e.target.classList.add("drop");
  }
});

main.addEventListener("dragleave", (e) => {
  if (e.target.classList.contains("drop")) {
    e.target.classList.remove("drop");
  }
});


Ensuite, nous traitons le début du glissement:



main.addEventListener("dragstart", (e) => {
  //    
  if (e.target.classList.contains("list-group-item")) {
    //      "dataTransfer"    ;
    // dataTransfer    HTML - text/html,
    //         
    e.dataTransfer.setData("text/plain", e.target.dataset.id);
  }
});


Maintenant, nous devons en quelque sorte garder une trace de l'élément sous celui qui a été traßné. Cela est nécessaire pour organiser arbitrairement les tùches dans la liste, c'est-à-dire permuter les tùches dans une colonne par endroits. Lors de la gestion de l'événement "mousemove", la méthode "elementFromPoint (x, y)" est utilisée pour cela. La beauté de cette interface est que pour déterminer l'élément "sous-jacent", il suffit de gérer l'événement "dragover":



//     "" 
let elemBelow = "";

main.addEventListener("dragover", (e) => {
  //    ;
  //      
  e.preventDefault();

  //     ;
  //   
  elemBelow = e.target;
});


Enfin, nous gérons l'événement "drop":



main.addEventListener("drop", (e) => {
  //     ,   dataTransfer
  const todo = main.querySelector(
    `[data-id="${e.dataTransfer.getData("text/plain")}"]`
  );

  //   ,     -   
  if (elemBelow === todo) {
    return;
  }

  //      , ,     
  if (elemBelow.tagName === "P" || elemBelow.tagName === "BUTTON") {
    elemBelow = elemBelow.parentElement;
  }

  //      ,     
  if (elemBelow.classList.contains("list-group-item")) {
    //   ,    :
    //    ;
    //       
    //       (  )
    //  
    const center =
      elemBelow.getBoundingClientRect().y +
      elemBelow.getBoundingClientRect().height / 2;
    //     
    // ,       
    // ,  
    if (e.clientY > center) {
      if (elemBelow.nextElementSibling !== null) {
        elemBelow = elemBelow.nextElementSibling;
      } else {
        return;
      }
    }

    elemBelow.parentElement.insertBefore(todo, elemBelow);
    //       
    //  ,     
    todo.className = elemBelow.className;
  }

  //    
  if (e.target.classList.contains("list-group")) {
    //      
    //        "" 
    e.target.append(todo);

    //     ""
    if (e.target.classList.contains("drop")) {
      e.target.classList.remove("drop");
    }

    //       ,    
    const { name } = e.target.dataset;

    if (name === "completed-list") {
      if (todo.classList.contains("in-progress")) {
        todo.classList.remove("in-progress");
      }
      todo.classList.add("completed");
    } else if (name === "in-progress-list") {
      if (todo.classList.contains("completed")) {
        todo.classList.remove("completed");
      }
      todo.classList.add("in-progress");
    } else {
      todo.className = "list-group-item";
    }
  }
});


C'est tout. Comme vous pouvez le voir, rien de compliquĂ©. Mais quelles sont les possibilitĂ©s d'ajouter de l'interactivitĂ© Ă  la page. Il reste Ă  attendre que les navigateurs mobiles mettent en Ɠuvre cette technologie, et tout le monde sera content.



J'espĂšre que vous avez trouvĂ© quelque chose d'intĂ©ressant pour vous-mĂȘme. Merci pour votre attention et bonne journĂ©e.



All Articles