I'm trying to implement a menu for a ios webkit based app in which the user touches/clicks and holds a menu button ('.menu_item'), after 500ms the sub menu opens (div.slide_up_sub_menu), and a user should be able to slide their finger/mouse up to a submenu li item and release.
<li class="menu_item">
ASSET MANAGEMENT
<div class="slide_up_sub_menu hidden_menu">
<ul class="submenu">
<li>Unified Naming Convention</li>
<li>Version Control</li>
</ul>
</div>
</li>
The application should then be able to detect which submenu item the touchend/mouseup event happened on. I'm binding a touchstart event to the menu item, waiting for 500ms, and afterwards telling the submenu to show. When a user releases their finger a touchend event should fire closing the submenu. If the user has stopped their touch on a submenu item it should be detected. Currently detection of which submenu item a mouseup event happened on works in Safari on the desktop:
$('ul.submenu li').live('mouseup', function(e){
console.log($(e.currentTarget)); //works on the desktop
});
but if I do the same using a touchend handler it doesn't work on an ipad:
$('ul.submenu li').live('touchend', function(e){
console.log($(e.currentTarget)); //never fires
});
if I look for every touchend event I can get a reference to the parent sub menu item when I end the touch on a submenu item:
$(document.body).bind('touchend', function(e) {
console.log($(e.target).html()); //logs ASSET MANAGEMENT
});
but no reference to the submenu item.
Does anyone have any idea why a touchend event is not being fired on the submenu items?
Thanks
touchend does not fire because touchcancel has fired earlier. If you want full touch evens handling you need to call
e.preventDefault()
in the callback of "touchmove" event, otherwise when a touchmove event occurs if the browser decides that this is a scroll event it will fire touchcancel and never fire touchend.
Maybe it's a bit late to answer.
This event will never fire, because touch events fire on the very element on which touchstart happened. So, to do what you want to, one solution is to use document.elementFromPoint method, to which you will send appropriate x and y coordinates.
If you don't need to use multitouch data, you may keep using mouseup on ipad.
further reading:
http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
For your "touchmove" event;
If you do not want to do anything on touchmove:
return false;
If you with to prevent the default behavior but execute some code on touchmove:
e.preventDefault();
This issue is more pronounced on iPads.
Related
I have some elements (widgets) in a div named tools. When I click on those elements I want to drag and drop them but not them directly. I clone the element in a new item named newnode and this is what I want to drag around the editor.
The trick that is working on desktop is to trigger an event on the new element, just $("#newnode").trigger(e);
$("#tools").on("mousedown touchstart",".widgets", function( e ) {
InitialX=$(this).offset().left;
InitialY=$(this).offset().top;
$("#newnode").css("left",InitialX);
$("#newnode").css("top",InitialY);
$("#newnode").css("width",$(this).width());
$("#newnode").css("height",$(this).height());
$("#newnode>svg").html($($(this).find("g")[0]).clone());
$("#newnode").show();
$("#newnode").trigger(e);
});
The code is working fine for desktop/computers but not for mobiles. When I send the trigger on desktop, the new cloned items start to receive events, being the new focused item, but when I do the same on mobiles tools/widget elements keeps receiving the events
I also added this code to see what was happening with the events, and yes, the old events is being sent to the non-cloned item.
$("#tools").on("blur focus focusin focusout load resize scroll unload click " +
"dblclick mousedown mouseup mousemove mouseover mouseout mouseenter " +
"mouseleave change select submit keydown keypress keyup error" +
"touchstart touchend touchmove", ".widgets",function(e){
console.log(e.type);
});
#newnode has z-index: 100000; to force to be over the original element.
to see an example on jsfiddle:
https://jsfiddle.net/hamboy75/v0br1cq6/44/
If you on computer, change to mobile in inspector, and you will see how it is different in log. Just click over a menu (duplicated will appear in gray color), without unclicking move a bit the mouse.
When viewed as computer newnode will receive events. When viewed as mobile widget will receive the events.
Finally i found a solution that works for my needs even if it is not exactly what i wanted (i wanted to receive all events in the new cloned item). Instead of adding the next line to receive the events in the #newnode element
$("#newnode").trigger(e);
i use the next code to receive events on #tools>.widgets, and if #newnode is visible resend the events to it using trigger.
$("#tools").on("mouseup mousedown mousemove touchstart touchend touchmove touchcancel", ".widgets",function(e){
if($("#newnode").is(":visible"))
$("#newnode").trigger(e);
});
I have a mobile web app, which uses a lot of click event handlers on buttons, etc. All of this works fine if the user really "clicks" (i.e. "touchdown-touchup") the button. However if the user does a short swipe, then the click event does not fire. This causes a lot of complaints from my users that the app doesn't register clicks/taps and that other apps work correctly.
Of course, I can get coordinates of the touch in ontouchstart and ontouchend and compute the distance - but I need to also know whether that distance is under the maximum that the browser would treat as 'click'. I do not want to switch to using touchstart/touchend events instead of click.
I used to use fastclick.js library for handling clicks/taps in the past, but now use native 'click' events with touch-action: manipulation. Is there any way of specify/controlling the maximum movement of the finger on the button that still registers as a 'click'?
Update based on comments. The application is very large and there are hundreds if not thousands of event handler assignments throughout it (the app has been developed over the last 8 years). Changing all of these is not practical, therefore I'm looking for a solution that would allow me to either set the threshold once globally or solve the problem with a global-like touchstart/touchend handlers.
I thought this was an interesting problem so I took a shot at solving it for you. In a way it's somewhat similar to the problem of preventing a click event when a dblclick happens.
Using a distance threshold for a "short swipe" seems, to me at least, problematic in that the threshold distance might be system dependent. Instead of that I decided to trigger on if the "click" event actually happens. I used mousedown as a simulated touchstart and mouseup as a simulated touchend. mouseup always happens before click so it is similar to touchend in that respect.
Normally if you "click" (mousedown) on an element and then move your mouse pointer off the element, the click event does not happen. This is much like the situation you describe as being a "short swipe". After a certain distance the click event just doesn't happen. The code below will send a click event for the button even if you mousedown on it, move the pointer off it and then mouseup. I believe that this would solve the problem if you used it for touchstart and touchend instead
// The pre-exisiting click handler
function handleClick(ev) {
console.log('button clicked. do work.');
}
document.getElementById('theButton').addEventListener('click', handleClick);
// our global "touch" handlers
var touchHandler = {
curPending: null,
curElem: null,
handleTouch: function handleTouch(ev) {
switch (ev.type) {
case 'mousedown':
// capture the target that the click is being initiated on
touchHandler.curElem = ev.target;
// add an extra click handler so we know if the click event happens
ev.target.addEventListener('click', touchHandler.specialClick);
break;
case 'mouseup':
// start a pending click timer in case the click event doesn't happen
touchHandler.curPending = setTimeout(touchHandler.pendingClick, 1);
break;
}
},
specialClick: function(ev) {
// the click event happened
// clear our extra handler
touchHandler.curElem.removeEventListener('click', touchHandler.specialClick);
// make sure we don't send an extra click event
clearTimeout(touchHandler.curPending);
},
pendingClick: function() {
// we never heard the click event
// clear our extra handler
touchHandler.curElem.removeEventListener('click', touchHandler.specialClick);
// trigger a click event on the element that started it all
touchHandler.curElem.click();
}
};
// using "mousedown" as "touchstart" and "mouseup" as "touchend"
document.addEventListener('mouseup', touchHandler.handleTouch);
document.addEventListener('mousedown', touchHandler.handleTouch);
<p>I work when clicked normally but I also work when
mousedown, drag pointer off me, mouseup</p>
<button id="theButton">Click Me</button>
I'm trying to handle touch/mouse events. So, I created this code:
myObject.addEventListener("touchstart", function(e){
e.stopPropagation();
e.preventDefault();
console.log("Touched");
mouseTouchDown(e);
});
myObject.addEventListener("mousedown", function(e){
console.log("Clicked");
mouseTouchDown(e);
});
function mouseTouchDown(e){
console.log("Some function.");};
I want to stop bubbling of touch event, so click won't be fired afterwards. It works on Chrome, but on Firefox I get in console:
Touched
Clicked
How can I stop mouse click firing after touch event?
I tried returning false, but it doesn't work.
This looks like a bug in the browser, your code looks fine.
See: https://bugzilla.mozilla.org/show_bug.cgi?id=977226.
What OS/version of Firefox are you testing this with?
Have u attached both events with same element?
If that is the case the error is not because of bubbling happening.while mouse click several events will happen like mousedown, touchstart ..etc.so if you want to avoid mouse click event occuring add preventDefault() in the mouse down.This will disable default mouse click event happening on that element.
Solution: https://github.com/alexgibson/tap.js
I have a conflict between 'touchend' and 'touchmove' events on the iPad in mobile Safari. I have images sitting next to each other like a gallery and they have a 'touchend' event attached to flip when touchend. However, you can also slide from one image to the other (like on iPhone sliding home screen to the next screen).
Now I can't figure out how to prevent the 'touchend' event from firing when I want to slide to the next image. Obviously, I don't want the image to flip if I slide, only if I tap.
My solution so far:
var img = $('.show-video');
var sliding = false;
img.bind('touchend', function(e) {
if (sliding === false){
Animate($(this), 'flip');
}
});
img.bind('touchmove', function(){
sliding = true;
$(this).bind('touchend', function(){
window.setTimeout(function(){
sliding = false;
}, 200)
})
});
````
I think this can be done much better.
You basically have two events when it comes to tapping the screen: touchstart and touchend. When a user touches the screen, the first event is captured, when he stops touching the screen, the second event is fired. The touchmove event should be captured to analyse the movement of the finger.
I'm not entirely sure what you're trying to say, but I'm assuming you you are capturing those events. What you can do is:
Instead of doing something on touchstart do it on touchend. This is more natural anyway, since something should happen after you lift the finger
After touchstart see if there is any movement using touchmove. If none and touchend is called, do what you wanted to do for a tap. If there was movement, do whatever you wanted to do for sliding.
I hope this helps.
I've made a workaround on this once.
You been already explained what are the events that are happening, and also you have a clue as a question.
For example lets say you have your gallery on a #galleryOverlay, and you scroller is inside it.
Then all you have to do is something like
$('#galleryOverlay').bind('touchstart touchmove touchmove', function(e){e.stopPropagation();})
So this way you keep your events inside your gallery, but you prevent them to happen outside.
Good luck
This library adds a 'tap' event which you can use to watch for a "tap" instead of "touchend".
https://github.com/alexgibson/tap.js
Is it possible to trigger a mouseout event on a link element using jQuery ?
I.e. Something of the sort
$(linkEle).mouseout()
I want this to work on an iPad, which even though does not have any mouse cursor, does actually have the event...
Yes, jquery has a mouseout event handler - http://api.jquery.com/mouseout/
$('some_selector_here').mouseout(function() {
// Do some stuff
}
$('some_selector_here').trigger('mouseout');
You might be able to use:
.trigger('mouseleave');
In the form of:
$('#elementToTriggerMouseLeaveOn').trigger('mouseleave');
References:
trigger().
I don't know about the ipad, but it works as you posted. http://jsfiddle.net/tESUc/
$(linkEle).mouseout();
or
$(linkEle).trigger('mouseout');
or
$(linkEle).trigger($.Event('mouseout'));
Try with tap event
tap - triggered after a tapping an pnscreen element.
http://www.roccles.com/?p=134
$('.link').live('tap',function(event) {
//TODO
});
mouse hover state does not exist on touchscreens
Mouse over/out events do not work as required on ipad. Take a look at touchstart/touchmove and touchend events which are specially for touch devices.
Something like this http://jsfiddle.net/hTYKQ/ Will work in ipad but in this fashion:
1st click to the element triggers the mouseenter function.
2nd click triggers stuff.. if it has stuff... like a link (
http://jsfiddle.net/qxM33/1/ i screwed the <a> href but you get
the point.)
Click outside the element triggers the mouseleave function.
What this story teaches is: jquery mouse over and mouse out functions work much like click functions in ipad.