I have horizontally scrolling panel (via overflow-x:scroll) and user should be able to scroll it by dragging (not just scrollbar, but the content itself too).
Using some draggable carousel library (e.g. owlcarousel) isn't an option as all of them use transforms instead of native scroll.
So my plan is:
bind mousedown event
change horizontal scroll offset on mousemove
stop all actions at mouseup
All is good on desktop. But the problem is mobile, as mobile browsers trigger fake mousedown and mousemove events - the scrolling is corrupted. If I call preventDefault in touchstart/move - fake mouse events stop firing, but pane isn't scrolling either.
Is there any way to prevent fake mousedown/move/up events on mobile without calling e.preventDefault()?
Thanks for any input!
Quoting W3C:
To avoid processing the same interaction twice for touch (once for the touch event, and once for the compatibility mouse events), developers should make sure to cancel the touch event, suppressing the generation of any further mouse or click events. Alternatively, see the InputDeviceCapabilities API for a way to detect mouse events that were generated as a result of touch events.
Unfortunately this doesn't seem to be available in any browser (yet?).
A more viable possibility is to disable touch scrolling on your element, and letting your mouse handling code take care of the scrolling:
.my-panel {
touch-action: none;
}
If this makes scrolling too janky on mobile, a hacky but possibly effective solution might be to ignore any mousemove event that follows shortly (say, within 100 ms) after a touchmove event.
Related
The objective is to have a component, in this case a carousel, that responds to the touchmove event when the user is moving his finger from left to right, but does not prevent the default scrolling up and down, even if the touch originates on the component in question, provided the user moves his finger on the y axis beyond a certain threshold.
Or, more plainly, I need to disable the browser scroll on touch devices, until the touchmove even says its OK, and re-enables it.
Despite several hours of tinkering, I've only been able to disable browser scroll completely or not disable it all. Conditionally disabling does not seem to want to work.
The basic logic is:
Record the coordinates on finger down (touch start).
On touchmove, check to see if the Y difference is greater than 200px.
If so, allow the default of browser scrolling, disable the touchmove listener.
If not, prevent the default of scrolling, scroll left/right.
I've been testing this on a late edition iPad mini. I have discovered the following:
Calling preventDefault() in the touchstart handler stops all browser scrolling for that touch, it cannot be re-enabled during drag.
Trying to conditionally prevent default in the dragmove handler behaves the same as preventDefault in the touchstart handler. That is, it blocks the drag, and ignores the condition!
A condition like this in dragmove:
if(Math.abs(MouseMoveDistanceY) < 200){
if(ev.preventDefault){ev.preventDefault();}
}
...blocks vertical scrolling permanently, even if the threshold gets exceeded!
In any case, how can I, on touch devices and in the dragmove handler, surrender control to the browser scroll when a condition is met? Something like "unPreventDefault()" would be great. Barring that, any ideas?
Just an idea, as I can't give you a direct answer so this is more of just looking at a different approach.. but have you looked at changing the page so that there is nothing to actually scroll when on the touch event? ie/ fix the height and add in overflow hidden.
On the Surface Pro 3 with Firefox only:
When making a swiping gesture with a single finger over an element, the browser will fire wheel events instead of touchmove or mousemove events. How do you stop the wheel behavior, and allow a single finger to always be treated as touch/mouse movement instead?
So I want to treat a single finger swipe as a series of mousemove or touchmove instead of as wheel events. I do not want a single finger swipe to scroll the page at all if swiping over this element. This is easy to do in Chrome and IE11. This seems not-possible right now in Firefox. Current I think this is a bug, but there may be something I'm missing.
Here is a simplistic example:
http://codepen.io/simonsarris/pen/PwbdRZ
var can = document.getElementById('can');
can.addEventListener('mousemove', function(e) {
// Will never happen on the Surface Pro 3 in Firefox
// Will happen in IE11 though
console.log('mouseMove')
});
can.addEventListener('touchmove', function(e) {
// Will never happen on the Surface Pro 3 in Firefox
// Will happen in Chrome though
console.log('touchMove')
});
// Stops the window from scrolling in firefox when you swipe on the element
// But stopping this does not allow the single touch gesture to register as mousemove or touchmove events
can.addEventListener('wheel', function(e) {
console.log('wheel')
e.preventDefault();
});
// consider also the 'DOMMouseScroll' event, though preventing this event will not stop firefox from panning the page.
Because I am preventing default in wheel, scrolling the page is stopped when one-finger swiping up or down
The window scrolling is stopped in firefox if you swipe in the red box, but no mousemove or touchmove events will fire. (yet mousemove will fire if you swipe horizontally instead of vertically)
Touch events are currently disabled in the desktop version of Firefox (36.0.4), though they will work while running Firefox in Metro mode or by explicitly enabling them via the dom.w3c_touch_events.enabled setting. See the MDN article on Touch events for more information.
I want to enable both vertical scrolling and horizontal swiping at the same time. I detect the touch position in touchmove instead of touchend, because I need real-time coordinates. Unfortunatelly, Chrome for Android fires touchcancel right after touchstart, so no more touchmove is fired. This touchcancel event can be avoided by calling event.preventDefault() from either touchstart or touchmove. But scrolling becomes disabled this way, so my first approach is to go without event.preventDefault(), and use some alternative method instead to detect touch position after touchcancel. Is it possible?
I'm wondering if it's possible to detect when a user has finished scrolling.
When dragging a scrollbar the old fashioned way, a mouseup event fires when the user lets go
When swiping on a touchscreen a touchend or MSPointerUp fires.
But if the user is scrolling via a mousewheel or some other trackpad gesture then what event[s] can be used to establish that the scrolling intention has ended?
I could do this with timeout mechanism in the scroll event handler, but ideally a single event signalling the end of the scroll is what I want.
I have a larger problem with swipes not being registered. And I believe its because the SDK's assume you would want to click an drag the entire viewport of Safari instead of any of the divs that could be in it.
How can I prevent this default?
I believe you want to listen for the touchmove event and call event.preventDefault() therein on any elements you don't want to contribute to viewport movement.
jquery example:
$('.interestingElements').on('touchmove', function(event) {
event.preventDefault();
});
In mobile safari, the default behavior for a touchmove involving a single touch is to slide the viewport around.
If two touches are involved, the default behavior is to trigger a gesture event. Preventing default on touchmove prevents the gesture event from ever firing. You can use the changedTouches array to find out how many touches are involved in this touchmove event. Good luck!