Touchscreen HTML Drag and Drop functionality

I have a task that involves dragging images and checking where they are dropped, then performing an action if they are in the right location. It works perfectly with a mouse, but it doesn't work on a touchscreen. How can I achieve this functionality on a touchscreen using Vue.js 2 or vanilla JavaScript?

Drag Item

<v-row v-for="(item, iterator) in Activity.drag_items" :key="item.class" :class="[item.class, item.status]" class="drag-item">
   <v-img 
      draggable
      @dragstart='startDrag($event, item, iterator)'
      :src="require(`@/assets/img/activities/activity_2/${item.item_img}`)"
      contain
      :class="item.status"
   ></v-img>
</v-row>

Drop Item

<a @drop='onDrop($event, Activity)' @dragover.prevent @dragenter.prevent></a>

On Drag Function

startDrag(evt, item, index){
        evt.dataTransfer.dropEffect = 'move';
        evt.dataTransfer.effectAllowed = 'move';
        evt.dataTransfer.setData('item', JSON.stringify(item));
        evt.dataTransfer.setData('index', index);
    }

On Drop Function

onDrop(evt, galaxy_location) {}

Answer №1

Currently, there is no direct support for the dataTransfer object in touch events. One workaround is to create methods that handle copying data and modifying it based on touch events. In my example, I demonstrate three methods to simulate drag and drop functionality using touch events.

During a touch start, necessary references are stored in an object, similar to dataTransfer.setData(). To mimic drag and drop behavior, the item is removed on touchstart and a new element is duplicated to follow the touch event.

  touchstartDrag(e, item, arr) {
  // iterate through the original array
  arr.forEach((el, i) => {
    if (el == item) {
      // store as reference
      this.touchDragItem = {
        item: item,
        index: i,
        arr: arr
      }
      // remove item from the array, or adjust opacity
      arr.splice(i, 1)
    }
  })
  let image = document.createElement("img"); // Create a new element
  image.setAttribute("id", "image-float");


  // get the image from the stored reference
  image.src = `https://cdn.quasar.dev/img/avatar${this.touchDragItem.item}.jpg`;
  image.width = 100
  image.height = 100

  // position the image according to the touch, can be enhanced to detect touch position within the image
  let left = e.touches[0].pageX;
  let top = e.touches[0].pageY;
  image.style.position = 'absolute'
  image.style.left = left + 'px';
  image.style.top = top + 'px';


  document.getElementById('app').appendChild(image);
},

On touchmove, the goal is to replicate the dragging sensation experienced during drag and drop. The previously created element in touchstart is retrieved and made to follow the touch movement.

    touchmoveDrag(e) {

  // retrieve the newly created image element while dragging
  let image = document.getElementById('image-float')
  // simulate the dragging effect by updating the position
  let left = e.touches[0].pageX;
  let top = e.touches[0].pageY;
  image.style.position = 'absolute'
  image.style.left = left + 'px';
  image.style.top = top + 'px';
  this.touchX = e.touches[0].pageX
  this.touchY = e.touches[0].pageY

},

For touch end, where the drop action is defined, manual detection of touch location relative to the drop zone is needed since there is no native drop event. Depending on whether the touch is inside or outside the drop zone, appropriate actions should be executed akin to dataTransfer.getData().

    touchendDrag(e) {
  // remove the image at touch end
  let image = document.getElementById('image-float')
  image.remove()
  // determine dropzone boundaries
  let rect1 = document.getElementById('top').getBoundingClientRect();
  let rect2 = document.getElementById('bottom').getBoundingClientRect()
  // check for overlap with drop zones
  var overlapTop = !(rect1.right < this.touchX ||
    rect1.left > this.touchX ||
    rect1.bottom < this.touchY ||
    rect1.top > this.touchY)
  var overlapBottom = !(rect2.right < this.touchX ||
    rect2.left > this.touchX ||
    rect2.bottom < this.touchY ||
    rect2.top > this.touchY)
  // retrieve stored reference
  let ex = this.touchDragItem
  // handle logic based on touch location
  if (!overlapTop && !overlapBottom) {
    ex.arr.splice(ex.index, 0, ex.item)
  } else {
    if (overlapTop) {
      if (this.top == ex.arr) {
        ex.arr.splice(ex.index, 0, ex.item)
      }
      if (this.top != ex.arr) {
        this.top.push(ex.item)

      }


    }
    if (overlapBottom) {
      if (this.bottom == ex.arr) {
        ex.arr.splice(ex.index, 0, ex.item)
      }
      if (this.bottom != ex.arr) {
        this.bottom.push(ex.item)

      }
    }
  }
  this.touchDragItem = null

},

This approach offers a basic implementation to mimic dnd API for touch events. There are numerous Vue.js drag and drop libraries available, such as sortable.js, catering to various use cases. However, if you wish to create your own touch drag functionality, this serves as a starting point.

Here is the complete working example:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

My function invocations seem to be malfunctioning

Originally, I wrote code without using functions to prototype and it worked perfectly fine: $(function() { $(".PortfolioFade img") .mouseover(function() { popup('PORTFOLIO'); var src = $(this).attr("src").rep ...

Angular JS - Selecting Directives on the Fly

I'm currently developing an application where users can choose from various widgets using a graphical user interface (GUI). My plan is to integrate these widgets as angular directives. THE CONTROLLER $scope.widgets = ['one', 'two' ...

Converting a JSON format with JavaScript

As the data for a simple online store is extracted from a headless CMS, it is structured in the following format: [ { "id": 12312, "storeName": "Store1", "googleMapsUrl": "https://example1.com", "country": "Australia", ...

What is the best way to dynamically update form fields in a database after making a selection in a <select> component?

Currently, I have a form that displays a list of stars (stellar objects) with a <select> control and two <inputs>. I am seeking guidance on how to populate the two inputs from the database when the user changes the selection in the <select&g ...

What is the best way to apply a mask to a textbox to format the date as MM/yyyy using a mask

In my asp.net application, I have a TextBox for entering Credit card date (month & year only). I tried using the 'TextBox with masked edit extender' and set Mask="99/9999" with Mask Type="Date. However, it is not working as expected - it only wor ...

Node.js encountered an error: Address already in use. The specified port 8080 is currently being utilized by another application

My application was not functioning properly, and upon running the command "npm start", an error was thrown: Error: listen EADDRINUSE: address already in use :8080 Even after restarting my EC2 instance and attempting the command again, I encount ...

Error encountered while utilizing MUI Button: Unable to access property 'borderRadius' from an undefined source

import React, { Component } from 'react'; import './App.css'; import Screen from './components/Screen/Screen'; import Button from './components/Button/Button'; import { MuiThemeProvider, createMuiTheme } from 'm ...

Switch up the background picture by chance when hovering over it with the mouse

Would you like to change the background image when hovering over an album, similar to Facebook's functionality? When hovering over an album, display a preview of 3-4 images randomly selected. How can this be achieved using jQuery? I have tried impleme ...

React-select allows for multiple selections within a component when the onChange function

I am currently utilizing react-select. Per the official documentation, it recommends using 'isMulti' to select more than one option. Below is the custom component that I have created. import React from 'react'; import { Form } from &ap ...

Unexpected behavior observed in the Python minifier Slimit

Attempting to decrease the size of some JavaScript code using the 'slimit' package in Python. import slimit slimit.minify('[1,2,3,4,5,6,7,8]') The above snippet executes without issue and outputs '[1,2,3,4,5,6,7,8]' import ...

The specified column `EventChart.åå` is not found within the existing database

I've been developing a dashboard application using Prisma, Next.js, and supabase. Recently, I encountered an issue when making a GET request. Prisma throws an error mentioning a column EventChart.åå, with a strange alphabet "åå" that I haven&apos ...

Vercel's Open Graph Image Generation encountering Failure - ERR_MODULE_NOT_FOUND

I am currently working on a project that utilizes NextJS and is deployed on Vercel. In an attempt to generate images, I am using the @vercel/og Open Graph Image generation library. However, each time the api route for the OG image is called, it leads to an ...

Exploring the power of Vue CLI service in conjunction with TypeScript

I've recently set up a Vue project using the Vue CLI, but now I am looking to incorporate TypeScript into it. While exploring options, I came across this helpful guide. However, it suggests adding a Webpack configuration and replacing vue-cli-service ...

Looking for a custom JavaScript color wheel with advanced features?

In search of a javascript color picker/wheel that allows users to easily select colors for our paint shop. Once the color is selected, it should automatically add the color value to the shopping cart. Our online store is operated using PrestaShop, so we ...

What method can I use to adjust the font style when it overlays an image?

Sorry if this is a bit unclear, but I'm trying to figure out how to change only a section of my font when it overlaps with an image while scrolling. If you visit this example website, you'll see what I'm referring to: For a visual represen ...

Tips for finding the *index* of distinct values in an array

My goal is to identify the indexes of unique values in an array. While I've come across various methods for removing duplicate values from an array, such as those found here: How to get unique values in an array I'm currently struggling to find ...

I am experiencing an issue with the initial for-loop in my code as it is not

Upon clicking the start button, the score button, as well as the try again or correct button, are not being updated. Can someone help me figure out what's wrong? I believe the issue lies within this section of the code: for (i=0; i<5; i++) { do ...

The spawn function in the child_process module throws an error with the code -2, specifically the

Hey there, I'm working on creating an npx command that allows me to run child commands within my package.json bin: "bin": { "malzahar": "./src/bin/malzahar.js" }, Below is the code from my malzahar.js: #! /usr/bin/en ...

What is the solution for incorporating multiple elements in knockout's applyBindingsToNode function?

I am currently using knockout applyBindingsToNode to dynamically add and remove elements in order to update my html. I need to cut the binding, which is why I am utilizing applyBindingsToNode. In an example I have provided, if you click on the button "Reb ...

Utilize express.router() to send a get request to a third-party API while including an API key

As I develop my react application, I am faced with the task of retrieving data from a third-party site that requires me to include an API key in the header as 'X-Auth-Token'. Currently, I am using the fetch() API from the client-side JavaScript ...