LePhenix47 / Drag-n-drop_Younes-Lahouiti

Coming soon, small d-n-d project where you can put in order some cards
https://lephenix47.github.io/Drag-n-drop_Younes-Lahouiti/
1 stars 0 forks source link

[CONCEPT] How to create a basic drag and drop #1

Open LePhenix47 opened 1 year ago

LePhenix47 commented 1 year ago

How to create a drag and drop (basic)

1) Select all the draggable elements

We need to add a “dragging” class that will serve for our CSS styling on our draggables on dragstart and remove it on dragend:

 const draggables = document.querySelectorAll(".draggable");
  const containers = document.querySelectorAll(".container");

  for (const draggable of draggables) {
    draggable.addEventListener("dragstart", () => {
      draggable.classList.add("dragging");
    });

    draggable.addEventListener("dragend", () => {
      draggable.classList.remove("dragging");
    });
  }

2) Select the containers containing the draggables

We need to add an event listener on dragover where we will:

for (const container of containers) {
    container.addEventListener("dragover", (e) => {
    //
      e.preventDefault();
      //
      const afterElement = getDragAfterElement(container, e.clientY);

      //
      const draggable = document.querySelector(".dragging");
      //
      const hasNoAfterElement = afterElement === null;
      if (hasNoAfterElement) {
        container.appendChild(draggable);
      } else {
        container.insertBefore(draggable, afterElement);
      }
    });
  }
LePhenix47 commented 1 year ago

How to get the closest element from our mouse?

1) Create a function that takes in a container and the mouse X or Y position

We will create a function that will take in a container as an HTMLElement and a position as a number for the X or Y coordinate of the mouse

function getClosestElement(container, position) {
  // Implementation steps go here
}

2) Select all the draggable elements

Inside the function, we select all the draggable elements that we are not dragging

function getClosestElement(container, position) {
  const draggableElements = [...container.querySelectorAll(".draggable:not(.dragging)")];

  // Implementation continues...
}

3) Create 2 variables to later make calculation

We're going to create a closestElement to store the element closest to the draggable by default set to null

And closestOffset to store the closest offset by default set to -∞ in order to ensure that the first valid offset encountered during the iteration will always be considered as the closest offset

function getClosestElement(container, position) {
  const draggableElements = [...container.querySelectorAll(".draggable:not(.dragging)")];
  let closestElement = null;
  let closestOffset = Number.NEGATIVE_INFINITY;

  // Implementation continues...
}

4) Iterate through each one of them and

a) Get the DOM Rect of the draggable

We can get the DOM Rect using the getBoundingClientRect

const draggableRect = draggable.getBoundingClientRect()

b) Calculate the currentOffset using a formula:

If vertical DnD:

const currentOffset = position - draggableRect.top - draggableRect.height / 2;

If horizontal DnD:

const currentOffset = position - draggableRect.left - draggableRect.width/2

In this example we'll take a vertical DnD:

function getClosestElement(container, position) {
  const draggableElements = [...container.querySelectorAll(".draggable:not(.dragging)")];
  let closestElement = null;
  let closestOffset = Number.NEGATIVE_INFINITY;

  for (const draggable of draggableElements) {
    const draggableRect = draggable.getBoundingClientRect();
    const currentOffset = position - draggableRect.top - draggableRect.height / 2;

    // Implementation continues...
  }
}

c) Verify if it's above the element

We need to verify it's above the element (currentOffset < 0) AND has the offset nearer to 0 (currentOffset > closestOffset). If that's the case, set the closestElement to the current draggable (not dragging) and also set the closestOffset to be the currentOffset

function getClosestElement(container, position) {
  const draggableElements = [...container.querySelectorAll(".draggable:not(.dragging)")];
  let closestElement = null;
  let closestOffset = Number.NEGATIVE_INFINITY;

  for (const draggable of draggableElements) {
    const draggableRect = draggable.getBoundingClientRect();
    const currentOffset = position - draggableRect.top - draggableRect.height / 2;

    const isAboveAfterElement = currentOffset < 0;
    const hasOffsetNearerToZero = currentOffset > closestOffset;

    if (isAboveAfterElement && hasOffsetNearerToZero) {
      closestOffset = currentOffset;
      closestElement = draggable;
    }
  }

  // Implementation continues...
}

d) Return the value of the closestElement

function getClosestElement(container, position) {
  const draggableElements = [...container.querySelectorAll(".draggable:not(.dragging)")];
  let closestElement = null;
  let closestOffset = Number.NEGATIVE_INFINITY;

  for (const draggable of draggableElements) {
    const draggableRect = draggable.getBoundingClientRect();
    const currentOffset = position - draggableRect.top - draggableRect.height / 2;

    const isAboveAfterElement = currentOffset < 0;
    const hasOffsetNearerToZero = currentOffset > closestOffset;

    if (isAboveAfterElement && hasOffsetNearerToZero) {
      closestOffset = currentOffset;
      closestElement = draggable;
    }
  }

  return closestElement;
}

This completes the step-by-step explanation of how to create a basic drag and drop functionality