I faced a similar issue recently and noticed that while inspecting the browser with the device tool, the browser uses touch events instead of mouse events, and it was working right. To address this, I created a JavaScript snippet to convert mouse events into touch events, and it worked flawlessly. You can use the following JavaScript code to make it work:
function triggerTouchEvent(eventType, originalEvent) {
console.log(`Triggering ${eventType} event for:`, originalEvent.target);
// Create the Touch object
const touchObj = new Touch({
identifier: Date.now(),
target: originalEvent.target,
clientX: originalEvent.clientX,
clientY: originalEvent.clientY,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 0,
force: 1.0,
});
console.log('Touch object created:', touchObj);
// Create and dispatch the TouchEvent
const touchEvent = new TouchEvent(eventType, {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: true,
});
console.log('Dispatching touch event:', touchEvent);
originalEvent.target.dispatchEvent(touchEvent);
console.log(`${eventType} event dispatched.`);
}
document.addEventListener('mousedown', function(e) {
console.log('Mouse down event detected.');
e.preventDefault(); // Prevent the default click behavior
triggerTouchEvent('touchstart', e);
}, { passive: false });
document.addEventListener('mousemove', function(e) {
console.log('Mouse move event detected.');
e.preventDefault(); // Prevent the default click behavior
triggerTouchEvent('touchmove', e);
}, { passive: false });
document.addEventListener('mouseup', function(e) {
console.log('Mouse up event detected.');
e.preventDefault(); // Prevent the default click behavior
triggerTouchEvent('touchend', e);
}, { passive: false });
Hi Eduardo, your solution worked well for me.
I found a way to address Raul's question about clicking inputs. You can add a condition that checks if the event target is an input, textarea, or otherwise form-related element. If the event is triggered on an input element, the code will not generate the custom touch event and will allow the default desktop behavior.
Hopefully Mendix will soon update the widget to make this an option for both device types (touch events only).
Just use this updated code:
function triggerTouchEvent(eventType, originalEvent) {
console.log(`Triggering ${eventType} event for:`, originalEvent.target);
// Create the Touch object
const touchObj = new Touch({
identifier: Date.now(),
target: originalEvent.target,
clientX: originalEvent.clientX,
clientY: originalEvent.clientY,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 0,
force: 1.0,
});
console.log('Touch object created:', touchObj);
// Create and dispatch the TouchEvent
const touchEvent = new TouchEvent(eventType, {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: true,
});
console.log('Dispatching touch event:', touchEvent);
originalEvent.target.dispatchEvent(touchEvent);
console.log(`${eventType} event dispatched.`);
}
function shouldSkipTouchEvent(target) {
// Skip inputs, textareas, select elements, buttons, and links
return target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.tagName === 'SELECT' ||
target.tagName === 'BUTTON' ||
target.tagName === 'A';
}
// Handle clicking outside inputs to blur (deselect) input fields
document.addEventListener('mousedown', function(e) {
console.log('Mouse down event detected.');
// If the click target is not an input or clickable element, blur the active element
if (!shouldSkipTouchEvent(e.target) && document.activeElement &&
(document.activeElement.tagName === 'INPUT' ||
document.activeElement.tagName === 'TEXTAREA' ||
document.activeElement.tagName === 'SELECT')) {
document.activeElement.blur(); // Remove focus from the active input
console.log('Active input blurred.');
}
if (shouldSkipTouchEvent(e.target)) {
console.log('Mouse down on input/button/link, skipping touch event.');
return; // Do not trigger touch events on input fields, buttons, or links
}
e.preventDefault(); // Prevent the default click behavior
triggerTouchEvent('touchstart', e);
}, { passive: false });
document.addEventListener('mousemove', function(e) {
console.log('Mouse move event detected.');
if (shouldSkipTouchEvent(e.target)) {
console.log('Mouse move on input/button/link, skipping touch event.');
return; // Do not trigger touch events on input fields, buttons, or links
}
e.preventDefault(); // Prevent the default click behavior
triggerTouchEvent('touchmove', e);
}, { passive: false });
document.addEventListener('mouseup', function(e) {
console.log('Mouse up event detected.');
if (shouldSkipTouchEvent(e.target)) {
console.log('Mouse up on input/button/link, skipping touch event.');
return; // Do not trigger touch events on input fields, buttons, or links
}
e.preventDefault(); // Prevent the default click behavior
triggerTouchEvent('touchend', e);
}, { passive: false });
Hi all,
Thanks Eduardo and Gina for your solutions.
I have modified the snippet so it will only create touch events for drag and drop items. As all draggable containers have a parent class with .dnd_draggable_item. This removes the need for specific exceptions for inputs.
EDIT: Instead of always triggering for mouse movements, now only do so if a draggable item is clicked
function triggerTouchEvent(eventType, originalEvent) {
// Create the Touch object
const touchObj = new Touch({
identifier: Date.now(),
target: originalEvent.target,
clientX: originalEvent.clientX,
clientY: originalEvent.clientY,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 0,
force: 1.0,
});
// Create and dispatch the TouchEvent
const touchEvent = new TouchEvent(eventType, {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: true,
});
originalEvent.target.dispatchEvent(touchEvent);
}
function hasDraggableItemClass(target) {
// Check if the target or any of its parents have the .dnd_draggable_item class
return target.closest('.dnd_draggable_item') !== null;
}
// Variable to track the dragging state
let isDragging = false;
function onMouseDown(e) {
if (!hasDraggableItemClass(e.target)) {
return; // Only process events on .dnd_draggable_item elements
}
e.preventDefault(); // Prevent the default mouse behavior
isDragging = true;
triggerTouchEvent('touchstart', e);
// Add event listeners for mousemove and mouseup
document.addEventListener('mousemove', onMouseMove, { passive: false });
document.addEventListener('mouseup', onMouseUp, { passive: false });
}
function onMouseMove(e) {
if (!isDragging) {
return;
}
e.preventDefault();
triggerTouchEvent('touchmove', e);
}
function onMouseUp(e) {
if (!isDragging) {
return;
}
e.preventDefault();
triggerTouchEvent('touchend', e);
isDragging = false;
// Remove the event listeners since dragging has ended
document.removeEventListener('mousemove', onMouseMove, { passive: false });
document.removeEventListener('mouseup', onMouseUp, { passive: false });
}
// Attach the mousedown event listener
document.addEventListener('mousedown', onMouseDown, { passive: false });