I have a page with a section to sketch a drawing in. But the touchmove events, at least the vertical ones, are also scrolling the page (which degrades the sketching experience) when using it on a mobile browser. Is there a way to either a) disable & re-enable the scrolling of the page (so I can turn it off when each line is started, but turn it back on after each is done), or b) disable the default handling of touchmove events (and presumably the scrolling) that go to the canvas the sketch is drawn in (I can't just disable them completely, as the sketching uses them)?
I've used jquery-mobile vmouse handlers for the sketch, if that makes a difference.
Update: On an iPhone, if I select the canvas to be sketched in, or just hold my finger for a bit before drawing, the page doesn't scroll, and not because of anything I coded in the page.
Set the touch-action CSS property to none, which works even with passive event listeners:
touch-action: none;
Applying this property to an element will not trigger the default (scroll) behavior when the event is originating from that element.
Note: As pointed out in the comments by #nevf, this solution may no longer work (at least in Chrome) due to performance changes. The recommendation is to use touch-action which is also suggested by #JohnWeisz's answer.
Similar to the answer given by #Llepwryd, I used a combination of ontouchstart and ontouchmove to prevent scrolling when it is on a certain element.
Taken as-is from a project of mine:
window.blockMenuHeaderScroll = false;
$(window).on('touchstart', function(e)
{
if ($(e.target).closest('#mobileMenuHeader').length == 1)
{
blockMenuHeaderScroll = true;
}
});
$(window).on('touchend', function()
{
blockMenuHeaderScroll = false;
});
$(window).on('touchmove', function(e)
{
if (blockMenuHeaderScroll)
{
e.preventDefault();
}
});
Essentially, what I am doing is listening on the touch start to see whether it begins on an element that is a child of another using jQuery .closest and allowing that to turn on/off the touch movement doing scrolling. The e.target refers to the element that the touch start begins with.
You want to prevent the default on the touch move event however you also need to clear your flag for this at the end of the touch event otherwise no touch scroll events will work.
This can be accomplished without jQuery however for my usage, I already had jQuery and didn't need to code something up to find whether the element has a particular parent.
Tested in Chrome on Android and an iPod Touch as of 2013-06-18
There is a little "hack" on CSS that also allows you to disable scrolling:
.lock-screen {
height: 100%;
overflow: hidden;
width: 100%;
position: fixed;
}
Adding that class to the body will prevent scrolling.
document.addEventListener('touchstart', function(e) {e.preventDefault()}, false);
document.addEventListener('touchmove', function(e) {e.preventDefault()}, false);
This should prevent scrolling, but it will also break other touch events unless you define a custom way to handle them.
The ultimate solution would be setting overflow: hidden; on document.documentElement like so:
/* element is an HTML element You want catch the touch */
element.addEventListener('touchstart', function(e) {
document.documentElement.style.overflow = 'hidden';
});
document.addEventListener('touchend', function(e) {
document.documentElement.style.overflow = 'auto';
});
By setting overflow: hidden on start of touch it makes everything exceeding window hidden thus removing availability to scroll anything (no content to scroll).
After touchend the lock can be freed by setting overflow to auto (the default value).
It is better to append this to <html> because <body> may be used to do some styling, plus it can make children behave unexpectedly.
EDIT:
About touch-action: none; - Safari doesn't support it according to MDN.
try overflow hidden on the thing you don't want to scroll while touch event is happening. e.g set overflow hidden on Start and set it back to auto on end.
Did you try it ? I'd be interested to know if this would work.
document.addEventListener('ontouchstart', function(e) {
document.body.style.overflow = "hidden";
}, false);
document.addEventListener('ontouchmove', function(e) {
document.body.style.overflow = "auto";
}, false);
I found that ev.stopPropagation(); worked for me.
To my surprise, the "preventDefault()" method is working for me on latest Google Chrome (version 85) on iOS 13.7. It also works on Safari on the same device and also working on my Android 8.0 tablet.
I am currently implemented it for 2D view on my site here:
https://papercraft-maker.com
this worked for me on iphone
$(".owl-carousel").on('touchstart', function (e) {
e.preventDefault();
});
the modern way (2022) of doing this is using pointer events as outlined here in the mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
Pointer events build on touchstart and other touch events and actually stop scroll events by default along with other improvements.
Related
When hovering over an element and then refreshing the page (without moving the mouse):
Chrome does not fire the mouseenter event on page load
Firefox does fire the mouseenter event on page load
Below is an example snippet. To reproduce the issue, hover over the div and then refresh the page. In Chrome, the div does not contain "mouseenter". In Firefox, it does.
Note that this does not work in the Stacksnippets environment since you need to click "run snippet" first. JSFiddle: https://jsfiddle.net/9fu6cx5d/7/
let div = document.getElementById('my-div');
div.addEventListener('mouseenter', function () {
div.innerHTML = 'mouseenter';
});
#my-div {
width: 150px;
height: 150px;
background-color: #aaaaaa;
}
<div id="my-div">
</div>
Which browser has the correct behaviour? How can I work around the difference in behaviour or at least make them both behave the same?
Chrome version: 59.0.3071.115 (Official Build) (64-bit)
Firefox version: 54.0 (64-bit)
As pointed out in the comments, Chrome's behavior is the correct one according to the specs. Below is an idea on how to work around the difference.
You can make sure you get the value right by checking whether the mouse is inside the bounds of the div on document load. Unfortunately there is no way in JS to check the mouse position without firing events, so you will have to resort to some hack involving CSS hover rules and checking against them on $(document).ready.
To quote this hilarious answer:
Overlay your page with a div that covers the whole document. Inside
that, create (say) 2,000 x 2,000 elements (so that the :hover
pseudo-class will work in IE 6, see), each 1 pixel in size. Create a
CSS :hover rule for those elements that changes a property (let's
say font-family). In your load handler, cycle through each of the 4
million elements, checking currentStyle / getComputedStyle() until
you find the one with the hover font. Extrapolate back from this
element to get the co-ordinates within the document.
N.B. DON'T DO THIS.
While you definitely shouldn't do this, the general idea of using non-effective hover styles for the sake of checking if an element is hovered without needing JS events is a good one if you just need to work around browser quirks. I'm using font-weight in the example below, but you can change it to whatever works for you.
The css
#my-div:hover {font-weight:700;}
The js
// Pseudocode!
var mouseIsInside = false,
div = $('#my-div');
$(document).ready(function(){
if (div.css('font-weight') === 700) {
mouseIsInside = true;
}
doStuffIfMouseInside();
});
div.on('mouseenter', function(){
mouseIsInside = true;
doStuffIfMouseInside();
})
function doStuffIfMouseInside() {
if (mouseIsInside) {
...
}
}
If you add (function(){})(); around your code it seems to work in both browsers.
It seems that firefox might be firing events before the dom is available causing problems with mousein/out events.
See: https://jsfiddle.net/9fu6cx5d/8/
I'm trying to prevent default scrolling behavior while still determining the number of pixels a user has attempted to scroll.
My objective is (at some vertical position on my page) to fix a navigation element to the top of the screen and hijack the scroll/swipe event to pull down a mobile menu when the user scrolls back up (so moving said element up and down by n pixels depending on how many pixels the user tries to scroll).
I am aware of the UX/accessibility concerns insofar as blocking native behavior, but the suits want what the suits want.
So far I have:
$('body').on({
'mousewheel' : function(e) {
e.preventDefault();
e.stopPropagation();
}
});
but am stumped as to how to access the number of pixels scrolled (since element/scroll offsets are no longer a guide).
Edit: Please note that this question is specifically asking for information regarding mouse/scroll actions while scrolling is blocked. Don't think this has been appropriately marked as duplicate.
This is browser-depended because of the mousewheel event you are using. This is because it is non-standard. Don't use it!
In Chrome (43.0) you get different properties with different values:
e.originalEvent.wheelDelta: -120
e.originalEvent.wheelDeltaY: -120
e.originalEvent.deltaY: 100
In IE (11.0), you can get only one property:
e.originalEvent.wheelDelta: -120
In Firefox (38.0.5), the capturing of the mousewheel event doesn't work at all.
Solution:
Use the wheel event (MDN reference). This event has the e.originalEvent.deltaY property in all browsers.
Before cancelling event propagation take the deltaY out of the original event like this
$('body').on({
'wheel' : function(e) {
console.log(e.originalEvent.deltaY);
e.preventDefault();
e.stopPropagation();
}
});
What is the best way to handle touch/pointer events on a surface tablet? I find its behavior quite confusing.
I wrote a script that just set all the event handlers to figure out how the events are fired (setting native event handlers, using jQuery for the UI):
$.each([
"touchstart","touchmove","touchend",
"mousedown","mousemove","mouseup",
"pointerdown","pointermove","pointerup",
"MSPointerDown","MSPointerMove","MSPointerUp"
], function(i,eventname) {
tester.addEventListener(this, function(e) {
msg(eventname,e);
if ($("#prevent").is(":checked")) {
e.preventDefault();
}
if ($("#stop").is(":checked")) {
e.stopPropagation();
}
if ($("#stopimmediate").is(":checked")) {
e.stopImmediatePropagation();
}
}, false);
});
Using the mousepad or desktop IE11, everything is fine. All three events (mouse, pointer, MSPointer) are fired in the expected order: down, move, up.
But, when I use the tochscreen
at first a pointermove fires, then pointer down. On the screen a rectangle appears around my finger.
repeated move events are firing while not moving my finger
firing no further events when I move my finger
pointerend event when removing finger
What am I doing wrong? Calling preventDefault, stopPropagation or stopImmediatePropagation does not change this behavior. Setting the css pointer-events: none; disables all events.
Complete script on codepen
You need to use the touch-action CSS property to prevent the browser's pan and zoom behaviours when the user drags accross the screen with a finger or stylus.
For example:
#prevent, #stop, #stopimmediate {
touch-action: none;
}
Try my touch gesture abstraction library, DeepTissue. IE's touch model is extremely simply because the pointer events abstract all the input modalities into a common API. Chrome is making things complicated, but that is another story :).
You really do not want to hook into all the events. I get that it would be confusing. That is why I wrote Deeptissue. It keeps you from having to write all the plumbing code and it chooses the best option and hooks into that event set.
I'm working on an iPad browser games and can't seem to lock the window. I've prevented scroll bars using typical overflow techniques, but the entire window still drags up and down (the new rubberband - type effect)
Is there a way to remove this window dragging?
Thannks
Bind to touchmove and preventDefault action which is scrolling
document.body.addEventListener('touchmove', function (ev) {
ev.preventDefault();
});
Or with jQuery
$('body').bind('touchmove', function (ev) {
ev.preventDefault();
});
EDIT: I've actually come across another solution that may be even easier, try this
body { overflow: hidden; }
...the reason I ask is that Safari has a bug in its implementation of scroll() that is breaking my UI.
Imagine a page:
<body>
<div id="huge" style="width: 4000px; height: 4000px;"></div>
</body>
...so that you get both horizontal and vertical scrollbars. Now, normally when you press the scrollbar, the page scrolls (vertically). For the purposes of our fancy UI we don't want that to happen, so we squash the keyDown event:
window.onkeydown = function(e) {
if(e.keyCode == 32)
{
return false;
}
};
This works great...unless we decide that instead of preventing scrolling altogether, we want our own, custom scrolling behavior:
window.onkeydown = function(e) {
if(e.keyCode == 32)
{
window.scroll(foo, bar); // Causes odd behavior in Safari
return false;
}
};
In other browsers (Chrome, Firefox), this will instantaneously move the window's scroll position to the desired coordinates. But in Safari this causes the window to animate to the desired scroll position, similar to the scrolling animation that takes place if you press the space bar.
Note that if you trigger this scroll off of any key OTHER than the space bar, the animation does not take place; the window scrolls instantly as in other browsers.
If you happen to be scrolling, say, 1000 pixels or more, then the animated scroll can induce some serious discomfort.
I'm scratching my head trying to find a way around this. I suspect that there isn't one, but I'm hoping some God of Javascript here can suggest something. I'd really like to be able to use the space bar for this command.
If you know where in the document you want to scroll to then you can simply use named anchors. Setting document.location to the anchor (e.g. #top, #div50 or whatever) should be very reliable.
Use document.documentElement.scrollTop = ... (and document.body in some browsers).