Some time ago I was struggling with drag and drop in web browsers on mobile devices. The default javascript events do not work on mobile. You can use only touch events.
In my case, I need to swap two images and also the IDs by doing drag and drop. Here an example:
div{
display:inline-block;
border:1px solid #0b79d0;
}
div, img{
width:120px;
height:120px;
}
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="a" draggable="true" ondragstart="dragStart(event)" src=".jpg"/>
</div>
<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="b" draggable="true" ondragstart="dragStart(event)" src=""/>
</div>
<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="c" draggable="true" ondragstart="dragStart(event)" src="/wp-content/uploads/2018/10/animals-for-dolphin-drawings-pencil-drawings-pinterest-verwandt-mit-delfine-zeichnen-100x100.jpg"/>
</div>
<script>
function allowDrop(ev){
ev.preventDefault();
}
function dragEnter(ev){
var element = document.getElementById(ev.target.id);
element.style.border = "dotted";
element.style.borderColor = "#0b79d0";
}
function dragLeave(ev){
var element = document.getElementById(ev.target.id);
element.style.border = "1px solid #0b79d0";
}
function dragStart(ev){
ev.dataTransfer.setData("src", ev.target.id);
var number = ev.target.id.replace ( /[^\d.]/g, '' );
ev.dataTransfer.setData("text/plain", number);
}
function drop(ev) {
ev.preventDefault();
var src = document.getElementById(ev.dataTransfer.getData("src"));
var srcParent = src.parentNode;
var tgt = ev.currentTarget.firstElementChild;
ev.currentTarget.replaceChild(src, tgt);
srcParent.appendChild(tgt);
var number1 = srcParent.id.replace(/[^\d.]/g, '');
var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');
var element = document.getElementById(ev.target.id);
element.style.border = "solid 1px #0b79d0";
var number = ev.target.id.replace(/[^\d.]/g, '');
}
</script>
Some time ago I was struggling with drag and drop in web browsers on mobile devices. The default javascript events do not work on mobile. You can use only touch events.
In my case, I need to swap two images and also the IDs by doing drag and drop. Here an example:
div{
display:inline-block;
border:1px solid #0b79d0;
}
div, img{
width:120px;
height:120px;
}
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="a" draggable="true" ondragstart="dragStart(event)" src="https://static.webshopapp./shops/073933/files/156288269/345x345x1/artibalta-white-tiger.jpg"/>
</div>
<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="b" draggable="true" ondragstart="dragStart(event)" src="https://yt3.ggpht./a-/AN66SAyfsmao4f1EEOqkBP2PgpSUcabPJXLZ1sLEnA=s288-mo-c-c0xffffffff-rj-k-no"/>
</div>
<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="c" draggable="true" ondragstart="dragStart(event)" src="https://kinderbilder.download/wp-content/uploads/2018/10/animals-for-dolphin-drawings-pencil-drawings-pinterest-verwandt-mit-delfine-zeichnen-100x100.jpg"/>
</div>
<script>
function allowDrop(ev){
ev.preventDefault();
}
function dragEnter(ev){
var element = document.getElementById(ev.target.id);
element.style.border = "dotted";
element.style.borderColor = "#0b79d0";
}
function dragLeave(ev){
var element = document.getElementById(ev.target.id);
element.style.border = "1px solid #0b79d0";
}
function dragStart(ev){
ev.dataTransfer.setData("src", ev.target.id);
var number = ev.target.id.replace ( /[^\d.]/g, '' );
ev.dataTransfer.setData("text/plain", number);
}
function drop(ev) {
ev.preventDefault();
var src = document.getElementById(ev.dataTransfer.getData("src"));
var srcParent = src.parentNode;
var tgt = ev.currentTarget.firstElementChild;
ev.currentTarget.replaceChild(src, tgt);
srcParent.appendChild(tgt);
var number1 = srcParent.id.replace(/[^\d.]/g, '');
var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');
var element = document.getElementById(ev.target.id);
element.style.border = "solid 1px #0b79d0";
var number = ev.target.id.replace(/[^\d.]/g, '');
}
</script>
So the dragStart event stores information, such as the image. However, such operation is not possible with touch events.
Now I was wondering, is there a way to do the same thing on mobile, by just imitating the drag events by using the touch events?
Share Improve this question asked Nov 29, 2018 at 1:20 PLAYCUBEPLAYCUBE 8754 gold badges17 silver badges25 bronze badges4 Answers
Reset to default 2For new ers, as for now, there is no dataTransfer for touch events (yet?). You can do the same functionality as DnD with abit more work. DnD just simplify the data transfer process and handle couple of stuff like detecting dragenter, but you can have the same with touch it's just you have to do all the dragenter detection stuff by yourself.
On touch start, I stored the reference of the drag element into variable, this is similar to dataTransfer.setData(), but added work here is to simulate the feeling of drag and drop by duplicating a new element to follow your touch event
function dragTouchstart(e){
//global variable that store the touch/drag element
imgId = e.target
let image = document.createElement("img"); // Create a new element
image.setAttribute("id", "image-float");
// get the image from the stored reference
image.src =imgId.src
image.width = 100
image.height = 100
// position the image to the touch, can be improve to detect the position of touch inside 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';
image.style.opacity = 0.5;
document.body.appendChild(image);
}
On touchmove, this is purely to simulate the dragging feeling that you get from dnd, get the element created in touchstart and make it follow your touchmove. but I also add the touchenter function to detect the dragenter
function dragTouchmove(e) {
// on touch move or dragging, we get the newly created image element
let image = document.getElementById('image-float')
// this will give us the dragging feeling of the element while actually it's a different element
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';
let touchX = e.touches[0].pageX
let touchY = e.touches[0].pageY
//apply touch enter fucntion inside touch move
dragTouchenter(e,touchX,touchY)
}
my touchenter function
function dragTouchenter(e,touchX,touchY){
let one =document.getElementById('1')
let two =document.getElementById('2')
let three =document.getElementById('3')
let id1 = one.getBoundingClientRect();
let id2 = two.getBoundingClientRect();
let id3 = three.getBoundingClientRect();
// to detect the overlap of touchmove with dropzone
var overlap1 = !(id1.right < touchX ||
id1.left > touchX ||
id1.bottom < touchY ||
id1.top > touchY)
var overlap2 = !(id2.right < touchX ||
id2.left > touchX ||
id2.bottom < touchY ||
id2.top > touchY)
var overlap3 = !(id3.right < touchX ||
id3.left > touchX ||
id3.bottom < touchY ||
id3.top > touchY)
//detect touchenter then apply style, if false, equal to touchleave
//could write a better function to take these elements in array and apply function accordingly
//but as for now I just write like this because faster by copy+paste
//get dropZoneId too
if(overlap1){
one.style.border = "dotted";
one.style.borderColor = "#0b79d0";
dropZoneId =one
}else{
one.style.border = "1px solid #0b79d0";
}
if(overlap2){
two.style.border = "dotted";
two.style.borderColor = "#0b79d0";
dropZoneId =two
}else{
two.style.border = "1px solid #0b79d0";
}
if(overlap3){
three.style.border = "dotted";
three.style.borderColor = "#0b79d0";
dropZoneId =three
}else{
three.style.border = "1px solid #0b79d0";
}
if(!overlap1 && !overlap2 && !overlap3){
dropZoneId = ''
}
/* console.log(dropZoneId.id) */
}
finally ontouchend will do all the logic that you will do with dataTransfer.getData()
function dragTouchend(e){
//remove dragged image duplicate
let image = document.getElementById('image-float')
image.remove()
dropZoneId.style.border = "1px solid #0b79d0";
//if outside any dropzone, just do nothing
if(dropZoneId == '') {
dropZoneId = ''
imgId = ''
}else{
// if inside dropzone, swap the image
let toSwap = dropZoneId.children[0]
let originDropzone= imgId.parentElement
originDropzone.appendChild(toSwap)
dropZoneId.appendChild(imgId)
dropZoneId = ''
imgId = ''
}
}
full working example based on OP is down below, changed an image because it appear to be broken
div {
display: inline-block;
border: 1px solid #0b79d0;
}
div,
img {
width: 120px;
height: 120px;
}
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="a" draggable="true" ondragstart="dragStart(event)" src="https://static.webshopapp./shops/073933/files/156288269/345x345x1/artibalta-white-tiger.jpg" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)"
/>
</div>
<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="b" draggable="true" ondragstart="dragStart(event)" src="https://yt3.ggpht./a-/AN66SAyfsmao4f1EEOqkBP2PgpSUcabPJXLZ1sLEnA=s288-mo-c-c0xffffffff-rj-k-no" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)"
/>
</div>
<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="c" draggable="true" ondragstart="dragStart(event)" src="https://cdn.quasar.dev/img/avatar1.jpg" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)" />
</div>
<script>
var imgId = 'test'
var dropZoneId = ''
function allowDrop(ev) {
ev.preventDefault();
}
function dragTouchstart(e) {
imgId = e.target
let image = document.createElement("img"); // Create a new element
image.setAttribute("id", "image-float");
// get the image from the stored reference
image.src = imgId.src
image.width = 100
image.height = 100
// position the image to the touch, can be improve to detect the position of touch inside 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';
image.style.opacity = 0.5;
document.body.appendChild(image);
}
function dragTouchmove(e) {
// on touch move or dragging, we get the newly created image element
let image = document.getElementById('image-float')
// this will give us the dragging feeling of the element while actually it's a different element
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';
let touchX = e.touches[0].pageX
let touchY = e.touches[0].pageY
//apply touch enter fucntion inside touch move
dragTouchenter(e, touchX, touchY)
}
function dragTouchenter(e, touchX, touchY) {
let one = document.getElementById('1')
let two = document.getElementById('2')
let three = document.getElementById('3')
let id1 = one.getBoundingClientRect();
let id2 = two.getBoundingClientRect();
let id3 = three.getBoundingClientRect();
// to detect the overlap of touchmove with dropzone
var overlap1 = !(id1.right < touchX ||
id1.left > touchX ||
id1.bottom < touchY ||
id1.top > touchY)
var overlap2 = !(id2.right < touchX ||
id2.left > touchX ||
id2.bottom < touchY ||
id2.top > touchY)
var overlap3 = !(id3.right < touchX ||
id3.left > touchX ||
id3.bottom < touchY ||
id3.top > touchY)
//detect touchenter then apply style, if false, equal to touchleave
//could write a better function to take these elements in array and apply function accordingly
//but as for now I just write like this because faster by copy+paste
//get dropZoneId too
if (overlap1) {
one.style.border = "dotted";
one.style.borderColor = "#0b79d0";
dropZoneId = one
} else {
one.style.border = "1px solid #0b79d0";
}
if (overlap2) {
two.style.border = "dotted";
two.style.borderColor = "#0b79d0";
dropZoneId = two
} else {
two.style.border = "1px solid #0b79d0";
}
if (overlap3) {
three.style.border = "dotted";
three.style.borderColor = "#0b79d0";
dropZoneId = three
} else {
three.style.border = "1px solid #0b79d0";
}
if (!overlap1 && !overlap2 && !overlap3) {
dropZoneId = ''
}
/* console.log(dropZoneId.id) */
}
function dragTouchend(e) {
//remove dragged image duplicate
let image = document.getElementById('image-float')
image.remove()
dropZoneId.style.border = "1px solid #0b79d0";
//if outside any dropzone, just do nothing
if (dropZoneId == '') {
dropZoneId = ''
imgId = ''
} else {
// if inside dropzone, swap the image
let toSwap = dropZoneId.children[0]
let originDropzone = imgId.parentElement
originDropzone.appendChild(toSwap)
dropZoneId.appendChild(imgId)
dropZoneId = ''
imgId = ''
}
}
function dragEnter(ev) {
var element = document.getElementById(ev.target.id);
element.style.border = "dotted";
element.style.borderColor = "#0b79d0";
}
function dragLeave(ev) {
var element = document.getElementById(ev.target.id);
element.style.border = "1px solid #0b79d0";
}
function dragStart(ev) {
ev.dataTransfer.setData("src", ev.target.id);
var number = ev.target.id.replace(/[^\d.]/g, '');
ev.dataTransfer.setData("text/plain", number);
}
function drop(ev) {
ev.preventDefault();
var src = document.getElementById(ev.dataTransfer.getData("src"));
var srcParent = src.parentNode;
var tgt = ev.currentTarget.firstElementChild;
ev.currentTarget.replaceChild(src, tgt);
srcParent.appendChild(tgt);
var number1 = srcParent.id.replace(/[^\d.]/g, '');
var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');
var element = document.getElementById(ev.target.id);
element.style.border = "solid 1px #0b79d0";
var number = ev.target.id.replace(/[^\d.]/g, '');
}
</script>
I strongly remend you to use a library to do that in mobile. I had to do relatively same behaviour and i used interact.js which is very handful and mobile ready.
If you just want to add touch gestures to your app, just look at Hammer.js. It is the main library to use touch events.
And if you use jQuery look at this topic.
I hope that helps.
Way to solve it some time ago (if time was main factor) was to implement touch punch - http://touchpunch.furf./
It's jquery-ui plugin that wires touch events to mouse events (so usually importing lib is enough - no need to change your code)
For educational purpose it's good what you're doing.
But for development it's better not reinvent the wheel and being capable of use a tool, that can help you over time.
So i remend you this Drag & Drop library. Easy to use, fun, lightweight and very well maintained.