純粋なJavaScriptでドラッグ&アンプ;ドロップを実装する方法

このチュートリアルでは、バニラJavaScriptでドラッグアンドドロップ効果を実装する方法を見ていきます。英語からの直訳である「プルアンドドロップ」は、効果の本質を反映しています。これは、インターフェイス要素をドラッグアンドドロップするユーザーにはよく知られています。





ドラッグアンドドロップは、さまざまな状況で必要になる場合があります。たとえば、次の場合です。





  • 要素の単純な視覚的再配置。





  •   .  —  -.





  • .  —  -    .





  •   .





  drag & drop   . .





HTML Drag and Drop API

  HTML5 API, drag & drop.           . API .





  ,   .  , ,  . , draggable



true



.





<div draggable="true">Draggable element</div>
      
      



,   .  MDN,    .





  • drag



    —  , .





  • dragstart



    —    .





  • dragend



    —   , .





  • dragover



    —  , ,   .





  • drop



    —    , ,     .





  .      . ,   , dragover



drop



        preventDefault



.  —     .   .





  API.   ,     . , DataTransfer



,   , .     ,   DataTransfer



 , , ,     ,   - .    MDN.





     ,  HTML Drag and Drop API.





 

   . .  —  , ul



.





<section class="tasks">
  <h1 class="tasks__title">To do list</h1>

  <ul class="tasks__list">
    <li class="tasks__item">learn HTML</li>
    <li class="tasks__item">learn CSS</li>
    <li class="tasks__item">learn JavaScript</li>
    <li class="tasks__item">learn PHP</li>
    <li class="tasks__item">stay alive</li>
  </ul>
</section>
      
      



:





body {
  font-family: "Tahoma", sans-serif;
  font-size: 18px;
  line-height: 25px;
  color: #164a44;

  background-color: #b2d9d0;
}

.tasks__title {
  margin: 50px 0 20px 0;

  text-align: center;
  text-transform: uppercase;
 }

.tasks__list {
  margin: 0;
  padding: 0;

  list-style: none;
}

.tasks__item {
  transition: background-color 0.5s;
  margin-bottom: 10px;
  padding: 5px;

  text-align: center;
  border: 2px dashed #b2d9d0;
  border-radius: 10px;
  cursor: move;
  background-color: #dff2ef;

  transition: background-color 0.5s;
}

.tasks__item:last-child {
  margin-bottom: 0;
}

.selected {
  opacity: 0.6;
}
      
      



  ,   — move



. ,    move



  , .





  selected



,  





drag & drop

 1.

 JavaScript.   draggable



  true



, .     JavaScript.





const tasksListElement = document.querySelector(`.tasks__list`);
const taskElements = tasksListElement.querySelectorAll(`.tasks__item`);

//        
for (const task of taskElements) {
  task.draggable = true;
}
      
      



,     .  , .





 2.    

dragstart



dragend



  .   selected



,   . .





tasksListElement.addEventListener(`dragstart`, (evt) => {
  evt.target.classList.add(`selected`);
})

tasksListElement.addEventListener(`dragend`, (evt) => {
  evt.target.classList.remove(`selected`);
});
      
      



 3.

     — . ,   dragover



. , ,           . :





  1. .





  2. .selected



      ,   dragover



    .





  3. , dragover



       ,  —     .





  4. , dragover



        . ,    ,    .





  5. , . ,  ,   .





  6.   .





:





tasksListElement.addEventListener(`dragover`, (evt) => {
  //      
  evt.preventDefault();

  //   
  const activeElement = tasksListElement.querySelector(`.selected`);
  //  ,       
  const currentElement = evt.target;
  // ,   :
  // 1.    ,   ,
  // 2.    
  const isMoveable = activeElement !== currentElement &&
    currentElement.classList.contains(`tasks__item`);

  //  ,   
  if (!isMoveable) {
    return;
  }

  //  ,    
  const nextElement = (currentElement === activeElement.nextElementSibling) ?
      currentElement.nextElementSibling :
      currentElement;

  //  activeElement  nextElement
  tasksListElement.insertBefore(activeElement, nextElement);
});
      
      



nextElement



  .      , , .





     — . ,   .      —   ,   .  .     .     , ,     . , nextElement



.





 4.

    ,   dragover



.     ,   . ,   ,     , .   ,  .





getNextElement()



.   , , .     ,  .





, getBoundingClientRect()



.       ,       . y



,   height



,



,   .





const getNextElement = (cursorPosition, currentElement) => {
  //      
  const currentElementCoord = currentElement.getBoundingClientRect();
  //      
  const currentElementCenter = currentElementCoord.y + currentElementCoord.height / 2;

  //     ,   
  //    —  DOM-
  const nextElement = (cursorPosition < currentElementCenter) ?
      currentElement :
      currentElement.nextElementSibling;

  return nextElement;
};
      
      



 . ,    — ,   .      ,  ,  DOM  .   , ,     .





,   ,    -     . ,  ,    .           DOM    dragover



.  ,      DOM.   , .





tasksListElement.addEventListener(`dragover`, (evt) => {
  evt.preventDefault();

  const activeElement = tasksListElement.querySelector(`.selected`);
  const currentElement = evt.target;
  const isMoveable = activeElement !== currentElement &&
    currentElement.classList.contains(`tasks__item`);

  if (!isMoveable) {
    return;
  }

  // evt.clientY —     ,
  //   
  const nextElement = getNextElement(evt.clientY, currentElement);

  // ,     
  if (
    nextElement && 
    activeElement === nextElement.previousElementSibling ||
    activeElement === nextElement
  ) {
    //  ,   ,      DOM
    return;
  }

  tasksListElement.insertBefore(activeElement, nextElement);
});
      
      



, :   ,  DOM  , ,  — !  —   .





  • HTML Drag and Drop API  MDN.  API.





  • Native HTML5 Drag and Drop.   API   .  .





  • How to make a Drag-and-Drop file uploader with vanilla JavaScript.  ,   drag & drop.





  • Drag & drop   . , HTML Drag and Drop API. , , - API .





  • 10 Best Drag And Drop JavaScript Libraries. JavaScript-,   drag & drop.






HTML Academy «React-» — JavaScript React.js . , . — . 95% . 27 .








All Articles