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 ;)
Related
I have a very specific problem. I'm writing a web-page for mobile phones which has a button on it. I'm detecting touchevent on every browser including IE, but on IE it's quite specific. After a few seconds it automatically ends. Can you somehow help me? Here is my code (modified one, but still not working properly):
if (window.navigator.pointerEnabled) {
tapButton.addEventListener("pointerup", function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener("pointerdown", function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
alert("pointerEnabled");
}
else if (window.navigator.msPointerEnabled) {
tapButton.addEventListener("MSPointerDown", function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener("MSPointerUp", function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
alert("mspointerEnabled");
}
else {
alert("ordinary touch");
tapButton.addEventListener('touchstart', function(e) {
e.preventDefault();
addClass(this, 'clicked');
buttonTouched = true;
}, false);
tapButton.addEventListener('touchend', function(e) {
e.preventDefault();
removeClass(this, 'clicked');
buttonTouched = false;
}, false);
}
And the html tag has in it:
-ms-touch-action: none !important;
touch-action: none !important;
but that does not help either.
I suspect you are running into a multi-touch issue...
Remember, touch events are not the same as mouse events. You can touch with more than one finger. What happens if you touch with one finger than add a second finger? You get two consecutive touchstart events. The same is probably true for touchend. I suspect user light is right that it's probably triggering a finger release incorrectly...
Please have a look at what is happening to the touches, changedTouches and targetTouches properties of the TouchEvent you get in your listener. I strongly suspect you'll see that there still is a 'finger' left touching... So it went from 2 touches to 1...
Making sure that the (no longer) touching finger is actually the one that's on the button etc is all a lot less simple than the good old mouseup and mousedown events were.
EDIT: I realize your problem is with IE and it's pointer events... However they work mostly the same in that they too support multi-touch (and could thus suffer the same issues). I'm not seeing a property akin to touches, but I do see a pointerId, which can give you the same info (at the cost of some bookkeeping on your end).
This MSDN page has some good info. Especially this code snippet is enlightening I think:
function pointerdownHandler(evt) {
evt.target.setPointerCapture(evt.pointerId);
}
This seems to confirm that, when a finger hits the surface, the contact point gets an ID, which is used to inform you which finger left the surface when you receive the pointerup event.
I'd add some logging that just prints the pointerId on pointerdown and pointerup and I'll bet you will quickly find your solution.
I have an HTML5 canvas that users interact with by clicking (or tapping).
$(function() {
var canvas = $('#annotations');
canvas[0].addEventListener('mousedown', clickCanvas);
canvas[0].addEventListener('touchstart', clickCanvas);
});
function clickCanvas(e) {
markLocation(e);
drawCanvas();
e.preventDefault();
}
This works as expected. However, on mobile devices if you tap and drag this registers a click at the point where the tap started (this makes sense because it's hooked up to touchstart). The page does not scroll as it normally does when you touch-drag outside the canvas.
When dragging, I would like the tap to be ignored and the whole page scrolled instead.
I was able to resolve this by:
Removing e.preventDefault();
Hooking up canvas[0].onselectstart = function () { return false; } to prevent text being selected when the canvas is double-clicked (this was what e.preventDefault() did in the first place)
Replacing touchstart with touchend
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?
I am working with JavaScript and jQuery in an UIWevView on iOS.
I'v added some javascript event handler that allow me to capture a touch-and-hold event to show a message when someone taps an img for some time:
$(document).ready(function() {
var timeoutId = 0;
var messageAppeared = false;
$('img').on('touchstart', function(event) {
event.preventDefault();
timeoutId = setTimeout(function() {
/* Show message ... */
messageAppeared = true;
}, 1000);
}).on('touchend touchcancel', function(event) {
if (messageAppeared) {
event.preventDefault();
} else {
clearTimeout(timeoutId);
}
messageAppeared = false;
});
});
This works well to show the message. I added the two "event.preventDefault();" lines to stop imgs inside links to trigger the link.
The problem is: This also seems to prevent drag events to scroll the page from happen normally, so that the user wouldn't be able to scroll when his swipe happens to begin on an img.
How could I disable the default link action without interfering with scrolling?
You put me on the right track Stefan, having me think the other way around. For anyone still scratching their head over this, here's my solution.
I was trying to allow visitors to scroll through images horizontally, without breaking vertical scrolling. But I was executing custom functionality and waiting for a vertical scroll to happen. Instead, we should allow regular behavior first and wait for a specific gesture to happen like Stefan did.
For example:
$("img").on("touchstart", function(e) {
var touchStart = touchEnd = e.originalEvent.touches[0].pageX;
var touchExceeded = false;
$(this).on("touchmove", function(e) {
touchEnd = e.originalEvent.touches[0].pageX;
if(touchExceeded || touchStart - touchEnd > 50 || touchEnd - touchStart > 50) {
e.preventDefault();
touchExceeded = true;
// Execute your custom function.
}
});
$(this).on("touchend", function(e) {
$(this).off("touchmove touchend");
});
});
So basically we allow default behavior until the horizontal movement exceeds 50 pixels.
The touchExceeded variable makes sure our function still runs if we re-enter the initial < 50 pixel area.
(Note this is example code, e.originalEvent.touches[0].pageX is NOT cross browser compatible.)
Sometimes you have to ask a question on stack overflow to find the answer yourself. There is indeed a solution to my problem, and it's as follows:
$(document).ready(function() {
var timeoutId = 0;
$('img').on('touchstart', function(event) {
var imgElement = this;
timeoutId = setTimeout(function() {
$(imgElement).one('click', function(event) {
event.preventDefault();
});
/* Show message ... */
}, 1000);
}).on('touchend touchcancel', function(event) {
clearTimeout(timeoutId);
});
});
Explanation
No preventDefault() in the touch event handlers. This brings back scrolling behavior (of course).
Handle a normal click event once if the message appeared, and prevent it's default action.
You could look at a gesture library like hammer.js which covers all of the main gesture events across devices.
I have this:
function dontMove(event) {
// Prevent page from elastic scrolling
event.preventDefault();
}
&
<body ontouchmove="dontMove(event);">
This, on the ipad, stops it from being draggable and does not allow that grey background the ipad has when you drag a whole page around to show up.
I have seen on another website that its possible to reverse that in another div, so that div is completely draggable again.
Does anyone know how to reverse it?
I have also tried using this to prevent it (in the document.ready):
document.ontouchmove = function(e){
e.preventDefault();
}
& this to enable it:
function doTouchMove(state) {
document.ontouchmove = function(e){
return state;
}
}
Then I put this to activate it.
<img ontouchmove="doTouchMove(state);" src="../jpeg/pages/01.jpg" class="touch"/>
This didn't seem to work
Is there anything wrong with this?
Or any other way that might work?
This is exactly why bubbles is slightly better(at least in my opinion).
bubbles is cross browser, so you should be able to replace.
e.preventDefault()
with
e.bubbles = false;
and then latter in your code, you could potentially reset bubbles to true.
If the above isn't an option then just ignore. :D
An alternative(if you are just working with an iPad) is to just reverse how the DOM works.
document.addEventListener('click', function(){}, true );
This will force the event to work in the other direction.
Document click execute
|
|
v
Element click execute
try this post, HTML with event.preventDefault and erase ontouchmove from body tag.
Mine looks like this
<script>
// Get touch move enevt from IOS
document.ontouchmove = function (event) {
if (!event.elementIsEnabled)
event.preventDefault();
};
// Get touch move enevt from IOS
function enableOnTouchMove(event) {
event.elementIsEnabled = true;
};
</script>
then enable ontouchmove on every tag you want. ie:
<div ontouchmove="enableOnTouchMove(event)" id="listing">
I managed to solve it with
$('#form1').unbind('submit').submit();
You can solve it by preventing the event only if it comes from the body:
document.ontouchmove = function(event){
if(event.target.tagName == "BODY"){
event.preventDefault();
}
}