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" />
Related
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 });
I am using three.js to allow users to create and edit a 3D model that involves using the scroll-wheel/two finger function, to zoom in and out. I want a second section of the page that is off the screen by default but the user can scroll down to see it. Preferably, this will be done only using the scroll bar, while the scroll-wheel can still be used.
For performance reasons, I'd prefer not to have to use something such as vue.js. Users provide data that remains on their computer that I'm using in both sections. This prevents me from just placing the data on another screen.
Overflow:hidden is out of the question because then I can not scroll to the bottom portion.
I tried using PreventDefault with several different EventListeners but none of them worked properly.
Below is the function that determines the size of the window and should include a function or the code to prevent scrolling.There aren't particular elements that shouldn't scroll, all of them shouldn't.
function onWindowResize() {
var viewWidth;
var viewHeight;
viewHeight=window.innerHeight-315;
//For Mobile
if(!UIactive && innerWidth < 640){
viewWidth= window.innerWidth;
//For Computer & Tablet
} else {
viewWidth= window.innerWidth -317;
if(window.innerHeight < 700){
viewHeight=window.innerHeight-52.67;
//Disable Scrollwheel
window.addEventListener('wheel',function(event){
//mouseController.wheel(event);
event.preventDefault();
}, false);
}
}
camera.aspect = (viewWidth) / (viewHeight);
camera.updateProjectionMatrix();
renderer.setSize(viewWidth, viewHeight);
UI.style.height= (viewHeight+'px');
}
Edit: I tried using the answer to a similar question. This did not achieve the desired result. I changed the code to be both window... and document... and a console.log statement included works but I can still scroll.
this.canvas.addEventListener('wheel',function(event){
mouseController.wheel(event);
return false;
}, false);
I then proceeded to try using preventDefault again and recieved the following error
Unable to preventDefault inside passive event listener due to target being treated as passive
Google Chrome docs say that,
With this intervention wheel/touchpad scrolling won't be blocked on document level wheel event listeners that do not need to call preventDefault() on wheel events.
Thus, you should apply the onmousewheel event on a specific div like so:
<div id="ScrollableDiv" style="height : 900px;background-color : red">
</div>
function stop(){
return false;
}
var div = document.getElementById('ScrollableDiv');
div.onmousewheel= stop;
Please refer this working fiddle.
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 ;)
I currently use the following test (taken out of Modernizr) to detect touch support:
function is_touch_device() {
var bool;
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
bool = true;
} else {
injectElementWithStyles(['#media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function(node) {
bool = node.offsetTop === 9;
});
}
return bool;
}
But some devices are both touch and mouse driven, so I want a seperate function to detect if a device has mouse support. What's a good way to do this check?
Ultimately my intention is to be able to do these:
if(is_touch_device())
if(has_mouse_support())
if(is_touch_device() && has_mouse_support())
There's a CSS media just for that!
You can check whether some device has a mouse by getting the value of the pointer CSS media feature:
if (matchMedia('(pointer:fine)').matches) {
// Device has a mouse
}
Because it's CSS you don't even need to use JavaScript:
#media (pointer: fine) {
/* Rules for devices with mouse here */
}
I am currently using the following (jQuery) and I haven't found any flaws yet on specific devices
$(window).bind('mousemove.hasMouse',function(){
$(window).unbind('.hasMouse');
agent.hasMouse=true;
}).bind('touchstart.hasMouse',function(){
$(window).unbind('.hasMouse');
agent.hasMouse=false;
});
Explanation: Mouse devices (also touch screen laptops) first fire mousemove before they can fire touchstart and hasMouse is set to TRUE. Touch devices (also for instance iOS which fires mousemove) FIRST fire touchstart upon click, and then mousemove. Then is why hasMouse will be set to FALSE.
The only catch is that this depends on user interaction, the value will only be correct after mouse move or touchstart so cannot be trusted to use on page load.
As mentioned in the question comments, specifically on https://github.com/Modernizr/Modernizr/issues/869, there is no good answer yet.
Answer by #josemmo is not working for me: on android phone with mouse attached matchMedia('(pointer:fine)').matches does not match.
Fortunately, I've succeeded with another media query: hover.
if (matchMedia('(hover:hover)').matches) {
// Device has a mouse
}
var clickHandler = (isMouseEventSupported('click') ? 'click' : 'touchstart');
function isMouseEventSupported(eventName) {
var element = document.createElement('div');
eventName = 'on' + eventName;
var isSupported = (eventName in element);
if (!isSupported) {
element.setAttribute(eventName, 'return;');
isSupported = typeof element[eventName] == 'function';
}
element = null;
return isSupported;
}
This is code from a friend/coworker of mine and he based it off of: http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
There is no immediate way of knowing, you'll have to wait for a touch event or a mouse event.
Presuming you want to detect either mouse or touch you can do the following: listen for touchstart and mousemove (the latter can fire on touch devices without an actual mouse). Whichever one fires first is 99% bound to be what you're looking for.
This does not take in account devices that actually have both.
document.addEventListener('mousemove', onMouseMove, true)
document.addEventListener('touchstart', onTouchStart, true)
function onTouchStart(){
removeListeners()
// touch detected: do stuff
}
function onMouseMove(){
removeListeners()
// mouse detected: do stuff
}
function removeListeners(){
document.removeEventListener('mousemove', onMouseMove, true)
document.removeEventListener('touchstart', onTouchStart, true)
}
As of 2021 pointerevents is implemented in all major browsers.
It gives you the posibility to dynamically detect pointerdevices mouse, touch and pen.
var is_touch_device=(('ontouchstart' in window)||
(navigator.maxTouchPoints > 0)||
(navigator.msMaxTouchPoints > 0));
var has_mouse_support=false;
document.addEventListener("pointermove", function(evt) {
var pointerType=evt.pointerType;
/*** Safari quirk ***/
if(pointerType==="touch"&&evt.height===117.97119140625
&&evt.height===evt.width)pointerType="mouse";
/*** Safari quirk ***/
has_mouse_support=(pointerType==="mouse");
}
It is of course dependent on the user moving the mousepointer.
Even safari on ipadOS 14.4.2 detects it, if AssistiveTouch is activated! But there seems to be some quirks in pointerType detection there. It detects pointerType as mouse first time the mouse is used and no touch has been performed. But if you later use touch, it will not detect and change to pointerType of mouse, if mouse is used after touch! No surprise!
Edit: After some messing around with ipadOS safari i have discovered that, when mouse is used after touch, the pointerevent width and height are the exact same, which in ipadOS 14.4.2 is 117.97119140625 every time mouse is used. This can be used as a not to reliable workaround. Who knows when they will change the width/height? Another peculiarity with pointermove detection in ipadOS, is that mouse move is only detected on buttom press on mouse.
It is not tested with pen on ipad/iphone. Who knows which quirks this will show?
Some websites have the annoying quality of auto-refreshing using some JS magic. This seems to bypass the browser's "do not auto refresh" options.
My question - is there a simple way of telling the browser (also via JS, in an add-on) "do not listen to them! Never auto-refresh!"?
EDIT: Just to make myself clear - I want to write a browser extension that prevents the current page from giving a "refresh" signal - whether auto-refresh or "standard" refresh given automatically by the page using some timer. I think that's a standard JS question...
Edit: Sorry, I missunderstood the question!
I think that what you need is this:
window.onkeypress = window.onkeyup = window.onkeydown = function( event ) {
event.preventDefault();
// or, in this case:
//return false;
};
When the window will receive the events of keypress, keyup e keydown signals that the default behavior MUST be prevented. If you return false, you will stop the event chain, preventing to execute anything too. The two lines have the same effect, give a try commenting each line.
Here is a improoved example:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Testing...</title>
</head>
<body>
<div id="clickDiv">Click me ;)</div>
<br/>
<input id="typeInput" value="Type something ! (here)"/>
</body>
<script type="text/javascript">
window.onkeypress = window.onkeyup = window.onkeydown = function( event ) {
event.preventDefault();
return false;
};
document.getElementById( "clickDiv" ).onclick = function( event ) {
console.log( "you clicked me and the window events related to the keyboard are still being prevented ;)" );
};
document.getElementById( "typeInput" ).onkeyup = function( event ) {
console.log( "you typed inside me and the window events related to the keyboard are still being prevented ;)" );
};
</script>
</html>
All popular browsers except Chrome and Safari already have options related to disabling auto-refreshing. To disable auto-refreshing in these browsers, check out this article: http://maketecheasier.com/disable-web-page-auto-refresh-for-various-browsers/2010/12/12
For a Chrome extension to stopping auto-refresh, check this out: https://chrome.google.com/webstore/detail/lcldcllmbokpbniijpnkpgoboadbfphb
Each browser can have this functionality turned off but I don't think there is a way to affect these changes through JS unless you write an "Extension".