Cross browser: prevent zoom on touchpad, but allow scroll - javascript

I would like to be able to prevent 2 finger zoom on trackpad "wheel" events, but still allow 2 finger scroll.
I have disabled zoom for mobile with:
<meta
name="viewport"
content="initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, width=device-width, viewport-fit=cover" />
but this does not prevent zoom on MacOS Safari and Chrome at least.
As some other posts suggest, doing a preventDefault on the "wheel" event means 2 finger scroll stops working on the trackpad. e.g.
const ignorePinchToZoomEvent = (event: WheelEvent) => {
if (event.ctrlKey) {
event.preventDefault();
}
}
document.addEventListener("wheel", ignorePinchToZoomEvent, { passive: false });
Is there an accepted, or acceptable way to do this?

Listening to the event on the window object resolves this issue, and only zoom events get the ctrlKey option. Not sure why document is different.
const ignorePinchToZoomEvent = (event: WheelEvent) => {
if (event.ctrlKey) {
event.preventDefault();
}
}
window.addEventListener("wheel", ignorePinchToZoomEvent, { passive: false });

Related

How to stop page scroll on mouse wheel interaction with input type=text [duplicate]

I need to make a webpage scrollable only by scrolling bar. I have tried to find how to catch scroll bar event, but as i see it is impossible. Currently i use this functions:
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
function wheel(e) {
preventDefault(e);
}
function disable_scroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = wheel;
}
But they are not very useful in my situation, because they block all scroll events. Do you have any ideas? I am thinking about it 3 days already and i didn't find any answer (and questions also). Thanks!
Prevent the window from scrolling with mouse wheel:
As document level wheel event listeners are treated as Passive, we need to mark this event listener to be treated as Active:
window.addEventListener("wheel", e => e.preventDefault(), { passive:false })
If the content of a <div> (or other element) is scrollable, you can prevent it like this:
document.getElementById('{element-id}').onwheel = function(){ return false; }
More info about scrolling intervention and using passive listeners to improve scrolling performance.
Outdated Method:
window.onwheel = function(){ return false; } // Old Method
more info (thanks #MatthewMorrone)
jQuery solution to prevent window scrolling with mouse wheel:
$(window).bind('mousewheel DOMMouseScroll', function(event){ return false});
If you want to prevent scrolling with mouse wheel in a single DOM element, try this:
$('#{element-id}').bind('mousewheel DOMMouseScroll', function (e) { return false; });
The DOMMouseScroll event is used in Firefox, so you have to listen on both.
I'm currently using this and it works fine. Scrolling using the bar works fine, but mouse wheel won't work.
The reason i'm doing it this way is that I have custom code to scroll the way I want, but if don't add any code it will just don't scroll on wheel.
window.addEventListener('wheel', function(e) {
e.preventDefault();
// add custom scroll code if you want
}

Is there a way to manually add delay to all user input DOM events?

For a research experiment I am trying to delay the user interface (create lag) on mobile browsers, as I am researching the effects of UI latency in webapps. For example, if a user scrolls the scroll action only happens (e.g.) 50ms later.
I've tried window.addEventListener(), and then window.dispatchEvent() after a certain delay with setTimeout(). Howver, in the case of scrolling, this did not scroll the page after dispatching the event manually. Scrolling with window.scrollBy() does work, but it fires its own event.
window.addEventListener('wheel', event => {
console.log(event)
if (event.cancelable) {
event.preventDefault()
event.stopImmediatePropagation()
setTimeout(() => {
let newEvent = new WheelEvent('wheel', {
deltaX: event.deltaX,
deltaY: event.deltaY,
deltaZ: event.deltaZ,
cancelable: false,
})
window.dispatchEvent(newEvent)
// window.scrollBy({ top: 20 })
}, DELAY)
}
}, {
capture: true,
passive: false,
})
This works for wheel events, but not for touch events.
If there a way to generalize this behaviour for all MouseEvents?
Ideally I would like to process all DOM MouseEvent's, like some sort of middleware.

IOS HTML disable double tap to zoom

I am designing a website primarily focused on data entry. In one of my forms I have buttons to increment and decrement the number value in a form field quickly. I was using
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
to disable the zoom which appeared to work using the Firefox app for IOS. However, when another user tested it with Safari, clicking on the button too fast resulted in zooming in on the page, distracting the user and making it impossible to increase the value quickly. It appears that as of IOS 10, apple removed user-scalable=no for accessibility reasons, so that's why it only works in 3rd party browsers like Firefox. The closest I found to disabling double tap zoom was this
var lastTouchEnd = 0;
document.addEventListener('touchend', function (event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
from https://stackoverflow.com/a/38573198
However, this disables quickly tapping altogether, which although prevents double tap zooming, also prevents the user from entering values quickly. Is there any way to allow a button to be pressed quickly, while also disabling double tap zooming?
The CSS property touch-action works for me. Tested on iOS 11.1.
button {
touch-action: manipulation;
}
See MDN for more info: https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
I ended up solving this problem by using the following code: See Greg's answer above
$(document).click(function(event) {
element = document.elementFromPoint(event.clientX, event.clientY);
if(document.getElementById("div_excluded_from_doubletap").contains(element)) {
event.preventDefault();
clickFunction(element);
}
});
I made a bit of a complicated answer, but it works very well and reliably at stopping double-tap and pinch-to-zoom and allows pretty much every other kind of interaction
let drags = new Set() //set of all active drags
document.addEventListener("touchmove", function(event){
if(!event.isTrusted)return //don't react to fake touches
Array.from(event.changedTouches).forEach(function(touch){
drags.add(touch.identifier) //mark this touch as a drag
})
})
document.addEventListener("touchend", function(event){
if(!event.isTrusted)return
let isDrag = false
Array.from(event.changedTouches).forEach(function(touch){
if(drags.has(touch.identifier)){
isDrag = true
}
drags.delete(touch.identifier) //touch ended, so delete it
})
if(!isDrag && document.activeElement == document.body){
//note that double-tap only happens when the body is active
event.preventDefault() //don't zoom
event.stopPropagation() //don't relay event
event.target.focus() //in case it's an input element
event.target.click() //in case it has a click handler
event.target.dispatchEvent(new TouchEvent("touchend",event))
//dispatch a copy of this event (for other touch handlers)
}
})
note: greg's answer does not work consistently (double-tapping on certain elements will still zoom)
If you want to prevent pinch-to-zoom, you'll need some JS and CSS (don't ask me why):
document.addEventListener('touchmove', function(event){
if (event.scale !== 1) event.preventDefault(); //if a scale gesture, don't
})
and
*{touch-action: pan-x pan-y} /*only allow scroll gestures*/
Add this to your header. This works for me.
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

jQuery ContextMenu event not working in IOS 8.2

I am using contextMenu event in .html sample, it will be fired when i long press on an DIV, but right now it is not working. Is something broken in latest IOS 8.2 version. Here is the sample code,
<head>
<title></title>
<script src="Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#content").on("contextmenu", function () {
alert("CM");
});
});
</script>
</head>
<body>
<div id="content" style="height:300px; width:300px; background-color:gray;"></div>
</body>
Here is the working sample
http://jsfiddle.net/4zu1ckgg/
Please someone help me with this.
Basically, on iOS, touch events are not emulated as mouse events.
Use touch events instead: "touchstart", "touchmove" and "touchend".
In your case, on iOS and contrary to Android, "contextmenu" is not triggered when screen is long touched.
To customize long touch on iOS you should use something like:
// Timer for long touch detection
var timerLongTouch;
// Long touch flag for preventing "normal touch event" trigger when long touch ends
var longTouch = false;
$(touchableElement)
.on("touchstart", function(event){
// Prevent default behavior
event.preventDefault();
// Test that the touch is correctly detected
alert("touchstart event");
// Timer for long touch detection
timerLongTouch = setTimeout(function() {
// Flag for preventing "normal touch event" trigger when touch ends.
longTouch = true;
// Test long touch detection (remove previous alert to test it correctly)
alert("long mousedown");
}, 1000);
})
.on("touchmove", function(event){
// Prevent default behavior
event.preventDefault();
// If timerLongTouch is still running, then this is not a long touch
// (there is a move) so stop the timer
clearTimeout(timerLongTouch);
if(longTouch){
longTouch = false;
// Do here stuff linked to longTouch move
} else {
// Do here stuff linked to "normal" touch move
}
})
.on("touchend", function(){
// Prevent default behavior
event.preventDefault();
// If timerLongTouch is still running, then this is not a long touch
// so stop the timer
clearTimeout(timerLongTouch);
if(longTouch){
longTouch = false;
// Do here stuff linked to long touch end
// (if different from stuff done on long touch detection)
} else {
// Do here stuff linked to "normal" touch move
}
});
Here is a the page explaining (among other) that touch events are not emulated as mouse events on every OS: http://www.html5rocks.com/en/mobile/touchandmouse/
Hope this helps, it took to me a long time to figured it out ;)

Prevent mouse wheel scrolling, but not scrollbar event. JavaScript

I need to make a webpage scrollable only by scrolling bar. I have tried to find how to catch scroll bar event, but as i see it is impossible. Currently i use this functions:
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
function wheel(e) {
preventDefault(e);
}
function disable_scroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = wheel;
}
But they are not very useful in my situation, because they block all scroll events. Do you have any ideas? I am thinking about it 3 days already and i didn't find any answer (and questions also). Thanks!
Prevent the window from scrolling with mouse wheel:
As document level wheel event listeners are treated as Passive, we need to mark this event listener to be treated as Active:
window.addEventListener("wheel", e => e.preventDefault(), { passive:false })
If the content of a <div> (or other element) is scrollable, you can prevent it like this:
document.getElementById('{element-id}').onwheel = function(){ return false; }
More info about scrolling intervention and using passive listeners to improve scrolling performance.
Outdated Method:
window.onwheel = function(){ return false; } // Old Method
more info (thanks #MatthewMorrone)
jQuery solution to prevent window scrolling with mouse wheel:
$(window).bind('mousewheel DOMMouseScroll', function(event){ return false});
If you want to prevent scrolling with mouse wheel in a single DOM element, try this:
$('#{element-id}').bind('mousewheel DOMMouseScroll', function (e) { return false; });
The DOMMouseScroll event is used in Firefox, so you have to listen on both.
I'm currently using this and it works fine. Scrolling using the bar works fine, but mouse wheel won't work.
The reason i'm doing it this way is that I have custom code to scroll the way I want, but if don't add any code it will just don't scroll on wheel.
window.addEventListener('wheel', function(e) {
e.preventDefault();
// add custom scroll code if you want
}

Categories

Resources