So I'm making a small HTML canvas application in HTML and JS, and ran into a problem with touch gestures.
I am working on allowing users to draw on a canvas with a touch screen. After adding support for multi touch I realized that the default touch gestures, such as scrolling or zooming, are still active.
I need to disable the default touch gestures for the entire page.
My first idea was to use Event.preventDefault() but it gives off an error and solves nothing.
Then I found this HTML attribute content="user-scalable=no" but that didn't work either.
So now I'm a bit stumped and would love some advice.
I know that you mentioned using event.preventDefault(), but it is a little confusing that this did not work for you, as it is the recommended way to do something like this.
Try this:
window.ontouchstart = function(event) {
if (event.touches.length>1) { //If there is more than one touch
event.preventDefault();
}
}
It does the following:
Runs whenever the user begins touching their device's screen (ontouchstart).
The function is given the event variable, which contains details about the event, including an array called touches with information about each touch in it. This array has an item for each place the screen is being pressed, so by getting it's length (event.touches.length) we get how many fingers the user is using - obviously we do not want to cancel single presses, as otherwise the user will not be able to do anything.
Finally, we call event.preventDefault() to prevent the default actions caused by these events. You could also implement your own custom actions above the event.preventDefault line.
For further information, you can take a look at this StackOverflow page.
i figured it out. the event.preventDefault() doesnt work at all but using <meta name="viewport" content="width=device-width, user-scalable=no"> this i got it to work properly. the issue was that i was using that attribute in my canvas instead of the meta so i had <canvas id="canvas" width="2000" height="2000" content="user-scalable=no">. thanks either way.
Edit: nope still not working. I was debugging on my pc with the chrome tools but seems to have had no effect on an actual touchscreen
Related
I am trying to do something similar to what embedded Google maps do. My component should ignore single touch (allowing user to scroll page) and pinch outside of itself (allowing user to zoom page), but should react to double touch (allowing user to navigate inside the component) and disallow any default action in this case.
How do I prevent default handling of touch events, but only in the case when user is interacting with my component with two fingers?
What I have tried:
I tried capturing onTouchStart, onTouchMove and onTouchEnd. It turns out that on FF Android the first event that fires when doing pinch on component is onTouchStart with a single touch, then onTouchStart with two touches, then onTouchMove. But calling event.preventDefault() or event.stopPropagation() in onTouchMove handler doesn't (always) stop page zoom/scroll. Preventing event escalation in the first call to onTouchStart does help - unfortunately at that time I don't know yet if it's going to be multitouch or not, so I can't use this.
Second approach was setting touch-action: none on document.body. This works with Chrome Android, but I could only make it work with Firefox Android if I set this on all elements (except for my component). So while this is doable, it seems like it could have unwanted side effects and performance issues. EDIT: Further testing revealed that this works for Chrome only if the CSS is set before the touch has started. In other words, if I inject CSS styles when I detect 2 fingers then touch-action is ignored. So this is not useful on Chrome.
I have also tried adding a new event listener on component mount:
document.body.addEventListener("touchmove", ev => {
ev.preventDefault();
ev.stopImmediatePropagation();
}, true);
(and the same for touchstart). Doing so works in Firefox Android, but does nothing on Chrome Android.
I am running out of ideas. Is there a reliable cross-browser way to achieve what Google apparently did, or did they use multiple hacks and lots of testing on every browser to make it work? I would appreciate if someone pointed out an error in my approach(es) or propose a new way.
TL;DR: I was missing { passive: false } when registering event handlers.
The issue I had with preventDefault() with Chrome was due to their scrolling "intervention" (read: breaking the web IE-style). In short, because the handlers that don't call preventDefault() can be handled faster, a new option was added to addEventListener named passive. If set to true then event handler promises not to call preventDefault (if it does, the call will be ignored). Chrome however decided to go a step further and make {passive: true} default (since version 56).
Solution is calling the event listener with passive explicitly set to false:
window.addEventListener('touchmove', ev => {
if (weShouldStopDefaultScrollAndZoom) {
ev.preventDefault();
ev.stopImmediatePropagation();
};
}, { passive: false });
Note that this negatively impacts performance.
As a side note, it seems I misunderstood touch-action CSS, however I still can't use it because it needs to be set before touch sequence starts. But if this is not the case, it is probably more performant and seems to be supported on all applicable platforms (Safari on Mac does not support it, but on iOS it does). This post says it best:
For your case you probably want to mark your text area (or whatever)
'touch-action: none' to disable scrolling/zooming without disabling
all the other behaviors.
The CSS property should be set on the component and not on document as I did it:
<div style="touch-action: none;">
... my component ...
</div>
In my case I will still need to use passive event handlers, but it's good to know the options... Hope it helps someone.
Try using an if statement to see if there is more than one touch:
document.body.addEventListener("touchmove", ev => {
if (ev.touches.length > 1) {
ev.preventDefault();
ev.stopImmediatePropagation();
}
}, true);
This is my idea:
I used one div with opacity: 0 to cover the map with z-index > map z-index
And I will detect in the covered div. If I detect 2 fingers touched in this covered div, I will display: none this div to allow user can use 2 finger in this map.
Otherwise, in document touchEnd I will recover this covered div using display: block to make sure we can scroll.
In my case I have solved it with
#HostListener('touchmove', ['$event'])
public onTouch(event: any): void {
event.stopPropagation();
console.log('onTouch', event);
}
I've been struggling for a while making websites compatible for desktop, tablet and mobile at once. There are loads of libraries to handle pointer events on touch devices. One of them is hand.js, which combines mouse and touch in pointer events, without me having to consider what device it is used for. Hand.js is working superb, exept for one problem. On touch devices, a pointerdown event is triggered even if it is intended as the start of a scroll or pinch.
There are solutions to work around this, but instead of adding more and more javascript, I considered ditching all libraries for touch and let the browser decide for itself.
To accomplish this, I'm thinking of using (or abusing :-) the <a href""> tag.
Instead of (for simplicity I use onclick inline instead of add an eventlistener) using:
<div class="myclass" onclick="start(1,2,3)">Hello</div>
I could use
Hello</div>
The benefit of this solution would be:
the device browser decides how to handle the click/touch, whilst zooming and scrolling is still enabled without triggering any unwanted action.
there is no need to register any event to the button.
I don't need any library
I've created a fiddle and (to my surprise) it is working. However, I'm no expert, hence my question:
Can I use this method without running into problems?
I'm trying to add touch functionality to an SVG.
I recognise the touch event using a jQuery like selector.
(I'm actually using angular JQLite - angular.element()):
.on("mousedown touch", function(event) {
On my desktop and in mobile Safari, there's no issue. The touches are recognised correctly.
It also responds correctly when saved as a bookmark, but when I include:
<meta name="apple-mobile-web-app-capable" content="yes" />
in my header, and save to the home screen.. the touch piece doesn't respond.
I'm wondering whether anybody knows the root cause of this or has a a workaround?
I'm using Angular 1.2.27 and iOS 8
For info, I worked around the issue by embedding an ng-click within the SVG itself.
This would tend to point to angulars JQLite implementation of click/moousedown/touchstart being the cause or possibly not supporting the same touch events as ng-click.
Add the following lines to your css-file.
svg {
pointer-events: none;
}
Now it should work.
I have made a simple carousal using an online script called "simplyScroll".
here is the link to the script:
http://logicbox.net/jquery/simplyscroll/#config
My Problem:
here is the link the page:
http://namdarshirazian.com/exhibition.php
Generally in desktop mode, when I click on each image, it runs a javascript and shows a popup. This javascript is written by myself. Simply a simple action of hide and show.
But when viewed with smartphone (android/firefox), it does not triggers click event. VERY STRANGE. Can anyone please help my why this does not work?
The click action is as simple as :
$("body").on("click", "element", function(){
});
You can experiment with touchup and touchdown events instead. It's actually a right mess caused by people worrying about touches being long. The fastclick library might smooth things out a bit.
i had the same problem when i did my website responsive for any device resolution, the solution is simple, you write your jQuery as standard but u have to include a script that will allow the jQuery to work on touch devices.
add this script into your website and check the magic result:
http://touchpunch.furf.com/
I am creating a simple web app that uses the new version of hammerJS for pinch zooming. I am having an issue with the default iOS page drag when a pinch starts. This seams to override the pinch event that I am attempting to capture. Any help stoping this while using hammerJS would be greatly appreciated.
PS: I have attempted to use event.preventDefault() to no avail.
You need to call preventDefault slightly differently to how you may be used to. Like so:
$('#your_element').on('pinch', function(event) {
event.gesture.preventDefault();
});
See this hammer.js example for another example.
Put this in your header:
<meta name="viewport" content="width=device-width,user-scalable=no">
Basically the user-scalable property controls if users are allowed to zoom the page. So once you disable it you can control the zoom with hammerJs.
More info here https://developer.mozilla.org/en-US/docs/Mobile/Viewport_meta_tag#Viewport_basics
What were you calling preventDefault on? I usually combine #Bertrand's answer with something like this
$(body).on('touchmove', function(e){
e.preventDefault();
});