How to change drag and drop from vertically to horizontally in JavaScript?

Viewed 1188

I am working on a sortable list, I am very new to javascript, I am able to vertically drag and drop. but i am not able do the same for horizontally. I saw a tutorial completed this.

For horizontal do i need to use X axis? If somebody can help me out with this code.

From what I can tell, the drop, along with the dragstart and dragend events, only are aware of the element they are going into. They can't tell if the mouse is on the top half of the dropzone, or the bottom half. How to basically create dropzone and switch element in case of horizontal

I want to create something like this

enter image description here

I don't want to do swapping between the words.

const list = document.querySelector('.list')
const listItems = document.querySelectorAll('.list-item')

// let dragIndex, dragSource

const getMouseOffset = (evt) => {
  const targetRect = evt.target.getBoundingClientRect()
  const offset = {
    x: evt.pageX - targetRect.left,
    y: evt.pageY - targetRect.top
  }
  return offset
}

const getElementVerticalCenter = (el) => {
  const rect = el.getBoundingClientRect()
  return (rect.bottom - rect.top) / 2
}

const appendPlaceholder = (evt, idx) => {
  evt.preventDefault()
  if (idx === dragIndex) {
    return
  }
  
  const offset = getMouseOffset(evt)
  const middleY = getElementVerticalCenter(evt.target)
  const placeholder = list.children[dragIndex]
  
  // console.log(`hover on ${idx} ${offset.y > middleY ? 'bottom half' : 'top half'}`)
  if (offset.y > middleY) {
    list.insertBefore(evt.target, placeholder)
  } else if (list.children[idx + 1]) {
    list.insertBefore(evt.target.nextSibling || evt.target, placeholder)
  }
  return
}

function sortable(rootEl, onUpdate) {
   var dragEl;
   
   // Making all siblings movable
   [].slice.call(rootEl.children).forEach(function (itemEl) {
       itemEl.draggable = true;
   });
   
   // Function responsible for sorting
   function _onDragOver(evt) {
       evt.preventDefault();
       evt.dataTransfer.dropEffect = 'move';
      
       var target = evt.target;
       if (target && target !== dragEl && target.nodeName == 'DIV') {
           // Sorting
       const offset = getMouseOffset(evt)
       const middleY = getElementVerticalCenter(evt.target)

      if (offset.y > middleY) {
        rootEl.insertBefore(dragEl, target.nextSibling)
      } else {
        rootEl.insertBefore(dragEl, target)
      }
     }
   }
   
   // End of sorting
   function _onDragEnd(evt){
       evt.preventDefault();
      
       dragEl.classList.remove('ghost');
       rootEl.removeEventListener('dragover', _onDragOver, false);
       rootEl.removeEventListener('dragend', _onDragEnd, false);


       // Notification about the end of sorting
       onUpdate(dragEl);
   }
   
   // Sorting starts
   rootEl.addEventListener('dragstart', function (evt){
       dragEl = evt.target; // Remembering an element that will be moved
       
       // Limiting the movement type
       evt.dataTransfer.effectAllowed = 'move';
       evt.dataTransfer.setData('Text', dragEl.textContent);


       // Subscribing to the events at dnd
       rootEl.addEventListener('dragover', _onDragOver, false);
       rootEl.addEventListener('dragend', _onDragEnd, false);


       setTimeout(function () {
           // If this action is performed without setTimeout, then
           // the moved object will be of this class.
           dragEl.classList.add('ghost');
       }, 0)
   }, false);
}
                       
// Using                    
sortable(list, function (item) {
   console.log(item);
});
.list {
  padding: 1rem;
}

.ghost {
    opacity: 1;
}

.list-item {
  background: #8bb3f4;
  padding: 0.5rem;
  border: 1px solid white;
  margin: 1px 0;
}

.placeholder {
  display: none;
  background: #303742;
  pointer-events: none;
}
<div class="list">
  <div class="list-item" draggable=true>
    Item 1
  </div>
  <div class="list-item" draggable=true>
    Item 2
  </div>
  <div class="list-item" draggable=true>
    Item 3
  </div>
  <div class="list-item" draggable=true>
    Item 4
  </div>
</div>

2 Answers

You just need to add display:inline-block to .list-item

const list = document.querySelector('.list')
const listItems = document.querySelectorAll('.list-item')

// let dragIndex, dragSource

const getMouseOffset = (evt) => {
  const targetRect = evt.target.getBoundingClientRect()
  const offset = {
    x: evt.pageX - targetRect.left,
    y: evt.pageY - targetRect.top
  }
  return offset
}

const getElementVerticalCenter = (el) => {
  const rect = el.getBoundingClientRect()
  return (rect.bottom - rect.top) / 2
}

const appendPlaceholder = (evt, idx) => {
  evt.preventDefault()
  if (idx === dragIndex) {
    return
  }
  
  const offset = getMouseOffset(evt)
  const middleY = getElementVerticalCenter(evt.target)
  const placeholder = list.children[dragIndex]
  
  // console.log(`hover on ${idx} ${offset.y > middleY ? 'bottom half' : 'top half'}`)
  if (offset.y > middleY) {
    list.insertBefore(evt.target, placeholder)
  } else if (list.children[idx + 1]) {
    list.insertBefore(evt.target.nextSibling || evt.target, placeholder)
  }
  return
}

function sortable(rootEl, onUpdate) {
   var dragEl;
   
   // Making all siblings movable
   [].slice.call(rootEl.children).forEach(function (itemEl) {
       itemEl.draggable = true;
   });
   
   // Function responsible for sorting
   function _onDragOver(evt) {
       evt.preventDefault();
       evt.dataTransfer.dropEffect = 'move';
      
       var target = evt.target;
       if (target && target !== dragEl && target.nodeName == 'DIV') {
           // Sorting
       const offset = getMouseOffset(evt)
       const middleY = getElementVerticalCenter(evt.target)

      if (offset.y > middleY) {
        rootEl.insertBefore(dragEl, target.nextSibling)
      } else {
        rootEl.insertBefore(dragEl, target)
      }
     }
   }
   
   // End of sorting
   function _onDragEnd(evt){
       evt.preventDefault();
      
       dragEl.classList.remove('ghost');
       rootEl.removeEventListener('dragover', _onDragOver, false);
       rootEl.removeEventListener('dragend', _onDragEnd, false);


       // Notification about the end of sorting
       onUpdate(dragEl);
   }
   
   // Sorting starts
   rootEl.addEventListener('dragstart', function (evt){
       dragEl = evt.target; // Remembering an element that will be moved
       
       // Limiting the movement type
       evt.dataTransfer.effectAllowed = 'move';
       evt.dataTransfer.setData('Text', dragEl.textContent);


       // Subscribing to the events at dnd
       rootEl.addEventListener('dragover', _onDragOver, false);
       rootEl.addEventListener('dragend', _onDragEnd, false);


       setTimeout(function () {
           // If this action is performed without setTimeout, then
           // the moved object will be of this class.
           dragEl.classList.add('ghost');
       }, 0)
   }, false);
}
                       
// Using                    
sortable(list, function (item) {
   console.log(item);
});
.list {
  padding: 1rem;
  
}

.ghost {
    opacity: 1;
}

.list-item {
  background: #8bb3f4;
  padding: 0.5rem;
  border: 1px solid white;
  margin: 1px 0;
  display:inline-block;
}

.placeholder {
  display: none;
  background: #303742;
  pointer-events: none;
}
<div class="list">
  <div class="list-item" draggable=true>
    Item 1
  </div>
  <div class="list-item" draggable=true>
    Item 2
  </div>
  <div class="list-item" draggable=true>
    Item 3
  </div>
  <div class="list-item" draggable=true>
    Item 4
  </div>
</div>

This is more a CSS issue than Javascript on my view... Just change your list class display to inline-flex and set a max-width to the items.

const list = document.querySelector('.list')
const listItems = document.querySelectorAll('.list-item')

// let dragIndex, dragSource

const getMouseOffset = (evt) => {
  const targetRect = evt.target.getBoundingClientRect()
  const offset = {
    x: evt.pageX - targetRect.left,
    y: evt.pageY - targetRect.top
  }
  return offset
}

const getElementVerticalCenter = (el) => {
  const rect = el.getBoundingClientRect()
  return (rect.bottom - rect.top) / 2
}

const appendPlaceholder = (evt, idx) => {
  evt.preventDefault()
  if (idx === dragIndex) {
    return
  }
  
  const offset = getMouseOffset(evt)
  const middleY = getElementVerticalCenter(evt.target)
  const placeholder = list.children[dragIndex]
  
  // console.log(`hover on ${idx} ${offset.y > middleY ? 'bottom half' : 'top half'}`)
  if (offset.y > middleY) {
    list.insertBefore(evt.target, placeholder)
  } else if (list.children[idx + 1]) {
    list.insertBefore(evt.target.nextSibling || evt.target, placeholder)
  }
  return
}

function sortable(rootEl, onUpdate) {
   var dragEl;
   
   // Making all siblings movable
   [].slice.call(rootEl.children).forEach(function (itemEl) {
       itemEl.draggable = true;
   });
   
   // Function responsible for sorting
   function _onDragOver(evt) {
       evt.preventDefault();
       evt.dataTransfer.dropEffect = 'move';
      
       var target = evt.target;
       if (target && target !== dragEl && target.nodeName == 'DIV') {
           // Sorting
       const offset = getMouseOffset(evt)
       const middleY = getElementVerticalCenter(evt.target)

      if (offset.y > middleY) {
        rootEl.insertBefore(dragEl, target.nextSibling)
      } else {
        rootEl.insertBefore(dragEl, target)
      }
     }
   }
   
   // End of sorting
   function _onDragEnd(evt){
       evt.preventDefault();
      
       dragEl.classList.remove('ghost');
       rootEl.removeEventListener('dragover', _onDragOver, false);
       rootEl.removeEventListener('dragend', _onDragEnd, false);


       // Notification about the end of sorting
       onUpdate(dragEl);
   }
   
   // Sorting starts
   rootEl.addEventListener('dragstart', function (evt){
       dragEl = evt.target; // Remembering an element that will be moved
       
       // Limiting the movement type
       evt.dataTransfer.effectAllowed = 'move';
       evt.dataTransfer.setData('Text', dragEl.textContent);


       // Subscribing to the events at dnd
       rootEl.addEventListener('dragover', _onDragOver, false);
       rootEl.addEventListener('dragend', _onDragEnd, false);


       setTimeout(function () {
           // If this action is performed without setTimeout, then
           // the moved object will be of this class.
           dragEl.classList.add('ghost');
       }, 0)
   }, false);
}
                       
// Using                    
sortable(list, function (item) {
   console.log(item);
});
.list {
  padding: 1rem;
  display: inline-flex;
}

.ghost {
    opacity: 1;
}

.list-item {
  background: #8bb3f4;
  padding: 0.5rem;
  border: 1px solid white;
  margin: 1px 0;
  max-width: 50px;
}

.placeholder {
  display: none;
  background: #303742;
  pointer-events: none;
}
<div class="list">
  <div class="list-item" draggable=true>
    Item 1
  </div>
  <div class="list-item" draggable=true>
    Item 2
  </div>
  <div class="list-item" draggable=true>
    Item 3
  </div>
  <div class="list-item" draggable=true>
    Item 4
  </div>
</div>

Related