A couple of months ago, I started a project with other peers to create a website that helps Korean learners, especially young kids, practice Korean through interactive games.
This project was initiated by a teacher currently working at a Korean school in the US. It aims to help students easily learn Korean by repeatedly practicing the consonants, vowels, and their combinations through various games.
The main target audience is young kids, so the game had to be simple to play while including many interactive elements to keep them engaged. One of the games, a word-matching game, requires users to find matching words and drag the answer to the correct spot.
The drag effect I implemented this time was simpler than the one I created for my portfolio website (which had a Trello-style drag-and-drop feature).
However, this new drag effect needed to work on both pointer and touch devices. Although the drag
event provides some helpful features, like dragImage
and dataTransfer
, I needed to explore the differences between drag events and touch events to ensure compatibility across devices.
Here are some key differences between touch events and drag events:
Triggering Timing and Sequence
Touch Events
- Touch events are triggered when a user touches the screen.
- The types of touch events include
touchstart
,touchmove
, andtouchend
. - These events are triggered immediately upon contact with the screen, giving them a faster response time.
Drag Events
- Drag events are mainly used for handling drag-and-drop actions with a mouse or pointer device.
- The types of drag events include
dragstart
,drag
,dragend
. - Drag events require the element to be draggable by setting
draggable="true"
in HTML, and they won’t start unless the element is set to be draggable. - Drag events are typically suited for desktop interactions and tend to be slower compared to mouse events or touch events, especially
drag
event might feel less responsive thanmousemove
ortouchmove
because they are controlled by the browser’s native drag-and-drop event system.
Key Features and Differences
Touch Events
- Touch events can detect multiple touch points
- Touch events are lightweight and optimized for mobile, where response speed is crucial.
- Used in several gestures, such as drag-and-drop, swipes, taps, and pinches.
- There aren’t many differences between handling events in vanilla JavaScript and React.
Drag Events
- Drag events require elements to be explicitly set as draggable.
- Drag events support data transfer, which is useful when transferring data, such as files or text, between applications of elements.
- Drag events are single-point and cannot track multiple gestures.
- Since React uses virtual DOM, direct DOM manipulation is discouraged, and the drop event in React should be handled differently, especially when DOM needs to be updated.
When transferring data like files or text, using the drag
event is convenient in many ways. However, when implementing an action to drag a specific element and move it to another position—especially if you want consistent behavior across multiple devices—I found that using both drag
and touch
events can make things significantly more complex and challenging to manage. This led me to explore simpler and more efficient methods to handle drag interactions across different devices.
So, I came up with a method that combines move
events with touch
events. Like the drag
event, the move
event is also a single-point event, but it shares many behavioral characteristics with touch events. By using these two events together, I could apply the same logic across different devices.
Here’s a quick summary of the advantages of using these two events together:
Direct Control Over Movement
Mouse and touch events both provide direct access to the element’s coordinates(clientX
and clientY
). This allows you to handle the element’s position directly, giving full control over the dragging behavior.
Easier to Implement and Maintain
With mouse and touch events, you can unify the logic by using the same handlers(e.g., mousedown
/ mousemove
for mouse and touchstart
/ touchmove
for touch). This creates simpler, more maintainable code and reduces redundancy.
Consistent Behavior Across Devices
Mouse events work seamlessly on desktop, while touch events handle mobile more effectively. Combining these makes it easier to ensure consistent, smooth interactions on both types of devices without additional fallback code.
When implementing a drag-and-drop effect using mouse and touch events, I listed out the key functionalities needed.
First, when the initial event is triggered on the target element, the pointer or touch point position must be saved to move the element along with the pointer or touch point. Then, tracking the pointer's movement and updating the element’s position is essential.
Finally, depending on the event outcome, the DOM or styling should be updated when the pointer reaches the target drop area.
To summarize:
- Save the pointer or touch point position within the drag element when the initial event (
mousedown
ortouchstart
) is triggered. - Update the drag element’s position during the movement using
mousemove
ortouchmove
events. - Pre-store the drop element's location and, when the pointer or touch point event (
mouseup
ortouchend
) occurs within this area, update the DOM or styling as needed upon drop.
Drag Section
My approach to creating the dragging effect involves attaching mousedown
and touchstart
event listeners to the draggable elements. When these events are triggered, the handler stores the pointer's screen position, the offset position of the pointer within the draggable element, and the dragging item’s information.
I also created a custom hook, useDragDrop
, to store drag and drop items and their positions. In this hook, once a dragItem
is assigned, mousemove
and touchmove
listeners are attached to track the pointer’s position, while mouseup
and touchend
listeners check the dropping position. As the pointer moves, the hook updates the pointer’s position, allowing the dragging element to follow the pointer accurately.
To show a grabbing motion when the drag starts, I applied grab
and grabbing
styles by checking that the dragItem
ID matched the current item’s ID.
Drop Section
Using the useDragDrop
hook allowed me to update the position of the dragging item smoothly.
One issue I encountered, however, was implementing a hover effect when the drag item hovers over the drop item. When I added the Tailwind class hover:bg-lighter
to the drop item (equivalent to &:hover { background-color: var(--bg-lighter); }
), the effect didn’t actually work as expected.
The reason for the issue was that since the dragging item is right under the pointer, the actual element that could trigger mouseover
effect was the drag item itself. So, I tweaked the logic little bit by checking if the Id
of dropItem from the useDragDrop
hook is the same as the current item’s Id
like below.
This condition enabled the hover effect to activate correctly when the pointer entered the dropItem
area.
The final step was to verify whether the dragItem
was dropped in the correct area when the mouseup
or touchend
event was triggered. If it was, the item moved to the drop zone; if not, it returned to its original position using the handleDrop
function from the useDragDrop
hook.
In summary, implementing drag-and-drop functionality across both mouse and touch devices required a custom approach using the useDragDrop
hook to handle events and positions efficiently. By combining mousemove
/touchmove
and mouseup
/touchend
events, I was able to create a smooth, cross-device drag-and-drop experience. This process highlighted the importance of tailoring interactions for different device types, and in the future, additional features like snapping to the drop area or adding transition effects could further improve the user experience.