Is it possible to use the native "pinch to zoom" on touch devices while using hammerjs to recognize swipe gestures?
I want users to be able to zoom in on images in the gallery (as they can natively when hammer event handler is not bound) and swiping to display the previous or next image.
hammertime.on('swipe', function(ev) {
if (ev.direction === 2) {
nextImage();
} else if (ev.direction === 4) {
prevImage();
}
});
Solution was to use touchAction = 'auto'
var hammertime = new Hammer(galleryEl, {touchAction : 'auto'});
Before doing this be sure to read http://hammerjs.github.io/touch-action/
When you set the touchAction to auto it doesnt prevent any defaults, and Hammer would probably break. You have to call preventDefault manually to fix this. You should only use this if you know what you're doing.
Related
There's a well known issue whereby iOS does a clever optimization to handle CSS :hover effects. Basically, it splits the ontouchstart "click" event into two: the first click is the "hover", and triggers animations, css effects, etc. Then, if the same link is clicked again, the event actually goes through.
eg:
<style>dd { height: 0 } dt:hover + dd { height: unset }</style>
<dt>Magic</dt>
<dd>Magic is cool</dd>
Most of the questions on here seem to be by people who want iPhone to behave like Android, skipping the one-two tap.
I want the opposite. My current idea is to detect that the user is on a touch device, and if so, have a system of classList add/remove/check to know whether the element has been clicked twice in a row.
Unfortunately, my android device fires off "onclick" events before firing "ontouchstart" events, and for some bizarre reason it also fires "onmouseenter" events.
This is what I have come up with so far
function enableLinkIfHoverOnly() {
let anchors = document.querySelectorAll("dt > a");
for(let i=0; i<anchors.length; i++) {
let anchor = <HTMLAnchorElement>anchors[i];
// disable iPhone correct behavior by adding dummy ontouchstart event
anchor.ontouchstart = (e) => true;
// simulate iPhone correct behavior with a class check
anchor.onclick = (e) => {
if('ontouchstart' in window) {
let hovered = document.querySelector(".hovered");
if(hovered !== anchor) {
if(hovered) hovered.classList.remove("hovered");
anchor.classList.add("hovered");
e.preventDefault();
}
}
}
}
}
Hoping that there is a better solution than this.
I am using PhoneGap and I need to catch a "keyboard is showing" event on android phones.
I've found some threads saying to use the "showkeyboard" event. (This one for example : Show hide keyboard is not working propery in android phonegap)
My question : Is this an android event usable with phonegap? Is this a simple phonegap event? Is this a browser event? Is this a classical javascript event?
I don't find any doc on this event, and I need it because it's also firing on orientation change...
EDIT: I've found this, saying it's from android but undocumented : https://issues.apache.org/jira/browse/CB-6154
These events are from Android but are not documented. I've encountered some trouble with this so I recommend not using them.
For information, in order to make my function work, I've done something like this (this is just the general idea):
this._keyboardTimer;
document.addEventListener('showkeyboard', function (e) {
clearTimeout(this._keyboardTimer); // keep only the last event
this._keyboardTimer = setTimeout(function(oldOrientation){
if (oldOrientation != getOrientation()) {
/* this is an orientation event */
} else {
/* keyboard is really opening */
}
}.bind(this, getOrientation()), 200);
}.bind(this), false);
function getOrientation() {
return ( (window.orientation == 90) || (window.orientation == -90) )
? 'landscape'
: 'portrait';
};
And I've done the same thing with the 'hidekeyboard' event. Hope this will help.
[EDIT] There's another problem (yirk!): keyboards may be slightly differents. If the keyboard changes for a smaller: the 'hidekeyboard' event is fired....
Since on mobile device browser such as safari , when user drag the screen, the whole website will move along with the finger. So the common solution is :
addEventListener('touchmove', function(e) { e.preventDefault(); }, true);
This will prevent any touchmove event . However, since the browser on mobile device has no scroll bar , when user want to scroll the dialog box of jquery ui , the touchmove event need to be permit. This statement will block that event.
addEventListener('touchmove', function(e) {
if (e.target.id != 'dialog' )
e.preventDefault();
return false;
}, true);
Then I add this statement to allow the dialog box to scroll. However, this solution has flaw because the background will be draggable and move along with user finger again. How to fix this problem? Thanks.
Been dealing with this all day and found this solution. When you want it to scroll the dialog on safari mobile on ipad/iphone/ipod, you need to use this:
if (/iPhone|iPod|iPad/.test(navigator.userAgent)) {
$('iframe').wrap(function () {
var $this = $(this);
return $('<div />').css({
width: $this.attr('width'),
height: $this.attr('height'),
overflow: 'auto',
'-webkit-overflow-scrolling': 'touch'
});
});
}
I'm working on a html5 canvas game, but I don't know how to handle touch events. When a user touch the screen, and drag, then the browser will scroll the page. I would like to prevent it, and get the touch start, and touch end position. Is it possible?
Thanks in advance
You need to override the default touch behaviour to stop touchevents dragging the page. Clearly, you'll need to handle them again if your page becomes larger than the available area, but as you're making a game, going to assume you're doing 100%/100% layout.
function preventBehavior(e) {
e.preventDefault();
};
document.addEventListener("touchmove", preventBehavior, {passive: false});
Edit: here's the W3C recommendation talking about touch events, which might be handy for you.
Due to breaking changes made in recent versions of Chrome, the above answers are no longer correct. Attaching a touch event listener to the document or body elements will cause the event listener to be registered in "passive" mode, which causes calls to preventDefault to be ignored.
There are two solutions:
The preferred solution is to use the CSS style touch-action to specify that no scrolling should happen (e.g. with the value "none")
In cases where this is not appropriate (e.g. if the type of interaction should change dynamically in a way that cannot be determined before the gesture begins) then the event listener must be registered with the third parameter set to { passive: false } (you should perform browser detection to ensure that this style is supported first, though).
If you don't want to use jQuery mobile or any other library then you can try this.
var startX, startY, endX, endY;
document.addEventListener("touchstart", function(e){
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
e.preventDefault();//Stops the default behavior
}, false);
document.addEventListener("touchend", function(e){
endX = e.touches[0].pageX;
endY = e.touches[0].pageY;
e.preventDefault();//Stops the default behavior
}, false);
canvas.addEventListener('touchstart', function(e)
{
alert(e.changedTouches[0].pageX + " " + e.changedTouches[0].pageY);
}
canvas.addEventListener('touchend', function(e)
{
alert(e.changedTouches[0].pageX + " " + e.changedTouches[0].pageY);
}
Here's a good article about touching and gesturing on mobile phones:
http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
Following solution preventing scroll when dragging AND at the same time usual scroll is working (when not dragging)
var scrollable = true;
var listener = function(e) {
if (! scrollable) {
e.preventDefault();
}
}
document.addEventListener('touchmove', listener, { passive:false });
onDragstartHandler() {
scrollable = false;
}
onDragendHandler(} {
scrollable = true;
}
Don't forget to bind onDragstartHandler and onDragendHandler to related elements
I wonder if its possible to prevent double-tap-to-zoom and double-tap-to-center on a specific HTML element in Safari iOS (iPad 1) ?
Because my small HTML5 game forces users to make fast clicks (or taps), which are interpreted as double clicks, and when it happens - the page changes zoom and centers itself.
Detecting double clicks (like in this answer - Safari iPad : prevent zoom on double-tap) smells bad..
Wrong answer #1:
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> - does not suit my purposes, because it will block any zoom.
Wrong answer #2: maybe would .preventDefault() on click event alone be enough for that ? - Does not have any effect.
There's no other way than catching the events you want to prevent, and call preventDefault() on them, as you had already more or less figured out.
Indeed, some particular CSS properties / values may change the global site behavior (fixed width or fixed, for example), but you're not safe from changes to the OS (see fixedhandling change in iOS5), nor do these changes necessarily prevent all behavior (pinch might be off, but not double-tapping).
So, the best way to disable default behavior only for double-tapping is to take advantage of the count of touches iOS provides: if we have only one contact, then we're tapping. Two, this means we're pinching.
The following setup code provides that functionality:
var elm = document.body; // or some selection of the element you want to disable
var catcher = function(evt) {
if (evt.touches.length < 2)
evt.preventDefault();
};
elm.addEventListener('touchstart', catcher, true);
Demo on jsFiddle.
Note: the third parameter (true) to addEventListener means that we want to capture events, that is catch them all, even for our descendant children.
I am preventing doubletaps like this:
var doubleTouchStartTimestamp = 0;
$(document).bind("touchstart", function (event) {
var now = +(new Date());
if (doubleTouchStartTimestamp + 500 > now) {
event.preventDefault();
}
doubleTouchStartTimestamp = now;
});
The elegance lies within the fact, that no timeouts are needed. I only update a timestamp. It only gets compared on the next touchstart. Works for me on iOS 6.
Doubletaps further down the dom are not affected.
The same works without jQuery, as well:
var doubleTouchStartTimestamp = 0;
document.addEventListener("touchstart", function (event) {
var now = +(new Date());
if (doubleTouchStartTimestamp + 500 > now) {
event.preventDefault();
}
doubleTouchStartTimestamp = now;
});
I wrote a jQuery plugin for the same purpose - selectively disabling double-tap zoom on given page elements (in my case, navigation buttons to flip pages) I want to respond to every tap (including double-tap) as a normal click event, with no iOS "touch magic", no matter how fast the user clicks it.
To use it, just run something like $('.prev,.next').nodoubletapzoom(); on the elements you care for. The principle it uses is to listen for consecutive touchstart events on a node within 500ms, and running event.preventDefault() on the second, unless other touches are active at the same time. As that preventDefault consumes both touches, we also synthesize the two "missed" click events for the node, so your intended touch action happens as many times as the user intended.
Apple has a lot of tips with specialized tags for webkit (Safari). View Official Docs
What iOS version/Safari browser are you using? That site most definitely does not let you double-tap. I found some CSS but haven't had time to try it as I'm about to step out:
body {
-webkit-text-size-adjust:none;
margin:0px;
}
div{
clear:both!important;
display:block!important;
width:100%!important;
float:none!important;
margin:0!important;
padding:0!important;
}
You will need to implement a double tap function and preventDefault on the second tap. Here is some tested code that uses global variables that should get you started:
<button id="test1">Double Tap Me!</button>
<div id="test2">EMPTY</div>
var elm1 = document.getElementById('test1');
var elm2 = document.getElementById('test2');
var timeout;
var lastTap = 0;
elm1.addEventListener('touchend', function(event) {
var currentTime = new Date().getTime();
var tapLength = currentTime - lastTap;
clearTimeout(timeout);
if (tapLength < 500 && tapLength > 0) {
elm2.innerHTML = 'Double Tap';
event.preventDefault();
} else {
elm2.innerHTML = 'Single Tap';
timeout = setTimeout(function() {
elm2.innerHTML = 'Single Tap (timeout)';
clearTimeout(timeout);
}, 500);
}
lastTap = currentTime;
});
And a fiddle: http://jsfiddle.net/brettwp/J4djY/
JQuery approach to disable Double Tap Zoom in MVC4
To Disable the double tap (double mouse down) functionality on iOS 1+ you need to catch the touchStart Event and create an override to prevent the zoom.
// Using Single script.js and JQuery.Mobile-1.2.0 UI each page in MVC gets assigned JQuery through delegates so you don't have to do a full refresh of the page allowing you to take advantage of the data-prefetch which loads the page in the DOM when the app loads for the first time
$(document).delegate("#CashRegister", "pageinit", function () {
// To Disable 'Pinch to Zoom' Note: don't implement gester event handlers if you want to
//keep pinch to zoom functionality NOTE: i use this as my pageinit is a delegate of a page
this.addEventListener("gesturestart", gestureStart, false);
this.addEventListener("gesturechange", gestureChange, false);
this.addEventListener("gestureend", gestureEnd, false);
//handle each event by disabling the defaults
function gestureStart(event) {
event.preventDefault();
}
function gestureChange(event) {
event.preventDefault();
}
function gestureEnd(event) {
event.preventDefault();
}
//Recreate Double Tap and preventDefault on it
$(this).bind('touchstart', function preventZoom(e) {
// recreate the double tab functionality
var t2 = e.timeStamp
, t1 = $(this).data('lastTouch') || t2
, dt = t2 - t1
, fingers = e.originalEvent.touches.length;
$(this).data('lastTouch', t2);
if (!dt || dt > 500 || fingers > 1) return; // not double-tap
e.preventDefault(); // double tap - prevent the zoom
// also synthesize click events we just swallowed up
$(this).trigger('click').trigger('click');
});
Actually, .preventDefault() definitely does work... using jQuery:
var InputHandler = {
startEventType : isTouch ? "touchstart" : "mousedown"
}
$(selector).bind(InputHandler.startEventType, function(evnt) {
evnt.preventDefault();
});
Your problem with trying to prevent on .click() is that the browser isn't throwing a "click" element. Safari only fires a click to help simulate a click... But when there's a double tab, Safair doesn't through a "click" element. Your event handler for .click() doesn't ever fire, and therefore the .preventDefault() doesn't fire either.