ipad/iphone droppable implementation - detecting element being touched
I am trying to develop a drag/drop functionality that works on both desktop and touch devices such as ipad/iphone.
I have no issues implementing the 'dragging' by simply binding the touchstart
, touchmove
and touchend
events on a given DOM element.
What I am struggling with is how to implement the 'droppable' part. So in other word开发者_StackOverflow社区s, how do I figure out on what DOM element was my draggable dropped 'over'??
You can assume the use of jQuery. However, I am NOT using jQueryUI - which currently doesn't work for touch events.
Obviously, you're page is going to have a lot of DOM elements. So I would suggest marking those that may have something dropped on them by using an HTML5 data attribute, i.e. . Then, in your touchend event handler, get the dropped element's position in the document using a combination of jQuery's .offset() and .height() and .width(). Once you have that, cycle through the marked "dropReceiver" divs to get their position. You're then ready to test whether the position of the dropped element intersects with the position of one of the dropReceiver elements.
For example: draggedElement.addEventListener("touchend", function(){ var draggedX1 = $(this).offset().left; var draggedX2 = $(this).width() + draggedX1; var draggedY1 = $(this).offset().top; var draggedY2 = $(this).height() + draggedY1;
$('div[data-dropReceiver=true]').each(function(){
var droppedX1 = $(this).offset().left;
var droppedX2 = $(this).width() + droppedX1;
var droppedY1 = $(this).offset().top;
var droppedY2 = $(this).height() + droppedY1;
if((draggedX1 > droppedX1 && draggedX1 < droppedX2 && draggedY1 > droppedY1 && draggedY1 < draggedY2) || (draggedX2 > droppedX1 && draggedX2 < droppedX2 && draggedY2 > droppedY1 && draggedY2 < draggedY2)){
var dropReceiver = $(this);
//do stuff with the dragged element and the element it was dropped on
break;
}
});
I'm pretty sure my lengthy if statement there is correct, but I might have made a typo or two so make sure you re-examine it if you find that this code isn't working.
Also, it's quite likely that the final solution you'll need to implement will be a bit more complex. If it's possible that the dragged element will overlap more than one possible dropReceiver, you can't simply break out of the .each() loop once you find an intersection. Instead, you're going to need to actually calculate how much of the elements are intersecting, and ultimately designate the true dropReceiver to be the one that has the greatest amount of intersection with the dragged element. However, the code I've provided above already gives you the tools you'll need to do this since it essentially provides the coordinates of the bounding rectangles of each element.
EDIT - in response to OP's first comment on this answer
It doesn't seem possible to have the dropReceivers fire a touch event. Even if you bind a touchstart event to them, it won't fire because the dragged element is not a child element of a dropReceiver (I assume), and it must have absolute positioning in order to be dragged. That means it will always be layered (like a higher z-index) above the dropReceivers. Thus, it will always be blocking the user's finger from actually touching the dropReceiver. And therefore the touchstart event will never fire on the dropReceiver.
HOWEVER, I do think there is an easier solution than what I came up with. A touch event on Mobile Safari includes the x and y coordinates of that touch. So rather than testing for the intersection of the dragged element and the dropReceiver, all you need to do is test if the touch point falls within the bounds of the dropReceiver, i.e.
if(touchpoint.x > droppedX1 && touchpoint.x < droppedX2 && touchpoint.y > droppedY1 && touchpoint.y < droppedY2){
//you've found the dropped element
}
This is definitely a better solution, since it eliminates the more complex situation where the dragged element could be intersecting with two dropReceivers. Since you're testing the single touch point instead, that will always fall within the bounds of only a single dropReceiver, so no intersection area comparisons would need to be made.
I urge you to consider using jQueryUI for this, so that you don't have to reinvent the wheel. You can resolve the touch event issue with using jQueryUI by using the jQuery.ui.touch-punch plug-in, which maps mouse events to touch events.
精彩评论