Click event doesn't work properly - javascript

I've made a little experiment with Html5 canvas and Javascript events.
Unfortunately, in a certain case, the javascript click event has an unexpected behavior.
This is the fiddle of the experiment : http://jsfiddle.net/Rh4kP/10/
When you click, often there is no output in the console. I observed this on Google Chrome 22.0.1 and Firefox 14.0.1
More weird, when you comment one of the "flip" line like this :
document.getElementById(hiddenCanvas).style.display = 'block';
// document.getElementById(displayedCanvas).style.display = 'none';
or
// document.getElementById(hiddenCanvas).style.display = 'block';
document.getElementById(displayedCanvas).style.display = 'none';
Click events work propertly !

The definition of a click event is this, per the Mozilla Development Network:
The click event is raised when the user clicks on an element. The click event will occur after the mousedown and mouseup events. [emphasis mine]
In other words, your 'flip' event is firing so quickly modifying content that your mousedown and mouseup events are not happening on the same DOM element, and so click does not fire.
Try this example (derived from yours) to see this in action. You'll notice that when your click event fires, the mousedown and mouseup events happen on the same DOM element. But when it doesn't happen, it is because mousedown and mouseup are happening on different DOM elements. Another test to run is slowing down your flip/flop. With a very slow timeout, you'll have less of a chance of encountering this problem (though the chance still exists - I would recommend using mouseup)
Try using just the 'mouseup' event instead. I tested w/ your example and it seemed to consistently work.
See the If you delete a DOM element, do any events that started with that element continue to bubble? question for some more information.

Related

Angular - issue with ng-blur on iPhone/iPad

I use AngularJS 1.4.7
I have editable input. If we focus on input and after click outside of this input field, script will do function "save()" in ng-blur. Everything work correctly, but on iPhone 5/6 and iPad don't work (not execute anything in ng-blur). I don't know why, but I deduce that problem is in focus/touch action. Someone know where is the problem?
Fix ng-blur not working on empty space and most elements with iOS
Goal
In my app, we wanted to hide open menus or search results when clicking away from
the menu or search box.
Problem
iOS Safari does not blur properly when clicking away from the current element.
Note
You do not need a special directive. The problem is not with ng-blur, which works fine. The problem is that a blur event never occurs because of Apple's unusual design on iOS, regardless of whether you use ng-blur or a native DOM blur event.
Reason for Issue
Apple has designed mobile Safari to perform event bubbling differently than desktop browsers do. According to official Apple docs, if an element that is clicked has no click listener attached to it, then no click event is triggered. Without a click event, focus does not change and no blur event occurs, so the current element does not lose focus even though the user really did click elsewhere on the page.
The blur will only happen if the user clicked an element with a click event listener. Some elements like hyperlinks and inputs have "built-in" event listeners for click, and so will always register a click, causing blur to work.
Normally in desktop browsers, the click event is blindly fired whether there is a registered listener or not in the element's DOM hierarchy. That is why ng-blur works as expected in desktop browsers, even on "empty space".
Apple says that for iOS Safari they only dispatch the event if they find an element registered to listen for it.
Apple Docs - go to Figure 6-4
Apple's documentation makes it sound like they are looking for a registered event listener and handled on the target element, but that is not the case. I found the answers in the following article, and more importantly, one of the comments to it.
Quirksmode Article on iOS event delegation
First, observe that placing a listener on the document or body does not cause the click event to be dispatched on iOS Safari, if the target element has no click event listener and handler. It works in most browsers, but not on iOS.
It appears that Apple is checking the DOM hierarchy up to the <body> tag before dispatching the click event, but is not checking the <body> tag or above. Therefore, you need a click event listener attached to an element somewhere below the <body>.
Solution/Workaround
The solution then is very simple. All you need to do is to wrap all of the page content in a "master" container element just below the <body> element, and put the listener there instead of on the body or the document. It can even be an empty handler function. Apple is only checking to see if one is registered. The reason for wrapping all content is so that no matter where in the page the user clicks, the bubble-up process will eventually reach the master container.
If you do that, then ng-blur will work as expected in iOS, because the click event on empty space (an element with no click event listener) will pass the iOS check when it finds the parent container's click event listener and a click event will be dispatched normally, like it would be in any other browser.
Caution
This solution effectively causes iOS Safari to see a click event listener on every DOM element's hierarchy, tricking it into dispatching the click event on every element, just like a normal browser. I do not know if Apple has a performance reason they avoid this in iOS, or if it is just an aesthetic/developer type preference (see example). You will be changing the default iOS behavior in your app by using this workaround.
For example, iOS users may accidentally select text on your page that they did not mean too, and which would not normally occur without a click and hold gesture.
I don't know why that problem occurs.
but do one thing. create one small directive 'fake-blur' . then call your method in that directive
App.directive('fakeBlur', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.blur(function(){
// call you function ex:
scope.save();
})
}
}
});
<input type="text" fake-blur/>

JQuery click event triggered on Chrome and FF device touch emulation?

In my Rails site, I have an element that I want to act as a link when clicked by a mouse (e.g. on a desktop), but not when touched on a touch-screen device (because there is also a hover behavior).
My understanding is that the JQuery .click event should not get triggered on a touch.
My coffeescript for setting the click handler is simply
...
$(this).click ->
location.href = url
...
(where "this" is the element in question)
I know this code works, because the click action works with the mouse. To ensure that it doesn't get triggered on a touch device, I use the device emulation in Chrome's Developer Tools to emulate a touch. However, when I do this, the method still fires and follows the link.
Is this a problem with the Chrome emulation or with my code? I want to know whether it will behave this way on real touch devices.
edit: Same thing happens with Firefox, so I'm thinking it's my code...
I realized that touch events trigger click events as well, later on in the event chain. To get the functionality I wanted, I kept the .click handler, but added a .touchstart handler where I called event.preventDefault() to short-circuit the rest of the event chain. This way, the .click handler fires for mouse clicks, but not for touches.

Which touch event should e.preventDefault() be called in to cancel a "click" on a touch device?

To prevent a click from firing, you can call e.preventDefault() in a click event handler. This lets you bind an event to mousedown, see if something happens like the mouse moving a certain distance, and if so, prevent the click when the mouse button is released.
A click handler with e.preventDefault() does not seem to prevent a click on certain touch devices however. (I am testing on an iPad mini). I have also tried calling e.preventDefault() in the touchend handler, which seems to do nothing.
Calling e.preventDefault() in the touchstart blocks the page from scrolling, and is also useless because it is too early to tell if the click should be cancelled. Only touchmove can decide if a cancel needs to occur.
The issue is, when a user drags a draggable element which is also a link, it needs to cancel the "click" of the link on fingerup. This works just fine on a desktop by cancelling the click event.
Is there an equivalent event I can cancel that stops an "armed click" from going off once the user lifts their finger up?
iOS is known to not register the click event property. This is likely due to the fact that iOS waits a little longer to determine how a click should be interpreted as:
the start of a pinching/zooming gesture
two or multiple-finger panning
the start of scrolling
the start of double tapping
a simple tap event (which we are trying to capture)
the start of touch-and-hold
Therefore, you can listen to the tap event instead, included in jQuery Mobile.
The jQuery Mobile tap event triggers after a quick, complete touch
event that occurs on a single target object. It is the gesture
equivalent of a standard click event that is triggered on the release
state of the touch gesture.
The tap event is not native, because it relies on conditionally listening upon touchstart to determine if the start and stop targets are the same: if so, jQuery Mobiel determines that it is indeed a genuine tap event and fires the custom, non-native event tap. This logic can be seen in the original source file, at line 75 onwards of ./js/events/touch.js.
An example usage is as follow:
$(selector).on('tap', function(e){
e.preventDefault();
});

Cancel IE9 touch scroll event and invoke Mousemove, possible?

On a Windows 7 computer with IE9 and a multitouch screen, like an HP TouchSmart, if you touch the screen on a page that is tall enough to have a scrollbar and move your finger up or down the page scrolls. This doesn't seem to fire any mousemove event. If you touch the screen and initially move left or right instead of up and down it does fire the mousemouse events.
I wan't to cancel this scrolling and cause the browser to invoke a normal mousemove event. Is this possible?
EDIT: There does not appear to be touch events in IE9 Does IE9 support Touch events on Windows 7?
EDIT 2:
A couple other points of interest about this. First is that browsers often fire a mousewheel event when scrolling is triggered by a gesture, this can often be caught and cancelled. Second is that in this particular case, you can prevent the scrolling on IE9 with this hack $(document).bind('mousedown mouseup click dblclick', function (e) { }); which as hacks sometimes do, does not make any sense to me - it may be possible to use fewer event bindings but I didn't have good access to a device to easily test.
After spending some time testing the various methods to suppress default event responses, I have no idea how to suppress the scroll event. You should, however, be able to fire the mousemove event from within a scroll event handler.
window.onscroll = function(e){
element.onmousemove();
}
//jquery
$(window).scroll(function(e){ element.mousemove(); } );
A primitive example.
Two things I suppose you could try to prevent auto-scroll: setting the overflow (or overflow-y) to hidden on you body element or as part of your onscroll handler, attempting to scroll back to your point of origin. Setting body's overflow to hidden will prevent scrolling and hide the scrollbar, but I'm not sure it's what you want.
Try touch events instead of mouse events.
I had the same issue with iPad. I had to add an e.preventDefault(); to the touchmove event. I did this only to the div where I was tracking interaction, not to the whole page.
element.ontouchmove = function(e){ e.preventDefault(); };
No idea about your device, but might be worth a try.

Click Delay On IPhone and Suppressing Input Focus

The webkit browser on iphone has a 300ms delay between when a user does a touch and when the javascript gets a click event. This happens because the browser needs to check if a user has done a double tap. My app doesn't allow zooming so a double tap is useless for me. A number of people have proposed solutions to this problem and they usually involve handling the 'click' on the touch end event and then ignoring the click generated by the browser. However, it doesn't seem to be possible to suppress a click that gets sent to an input element. This can cause a problem if you have a dialog that opens above a form then a user hits the close button and their click gets routed to an input element when the form disappears.
Example with jqtouch (for iphone only)
You have to capture your event on touchstart if you want to get the fastest possible responsiveness. Otherwise you'll be doomed with this input lag.
You have to remember though that capturing event on touchstart and responding to it makes it impossible to cancel action by dragging your finger out of responsive area.
I have personally used this in my PhoneGap html/js based iphone application and it worked perfect. The only solution to give this almost-native feel.
Now regarding your problem - have you tried to stop the propagation of the event? It should solve your problem.
$('.button').bind('touchstart', function(e){
e.stopPropagation();
// do something...
});
hope it helps,
Tom
My colleagues and I developed an open source library called FastClick for getting rid of the click delay in Mobile Safari. It converts touches to clicks and handles those special cases for input and select elements cleanly.
It's as easy as instantiating it on the body like so: new FastClick(document.body), then listening for click events as usual.
I made Matt's FastClick a jquery plugin:
stackoverflow link
Just had a comment about the onClick handler being called without the necessary scope being passed. I updated the code to make it work.
There also seems to be a problem when input elements lie under the ghost event's position: the focus event gets triggered without being busted.
I see two problems in the question. One is handling the delay in click and the other is handling input focus.
Yes, both of these have to be handled properly in mobile web.
The delay in click has deeper reasons. The reason for this 300ms delay is explained very well in this article.
Responsiveness of the click event.
Thankfully this problem is well known and solved by many libraries.
JQTouch, JQuery Mobile,
JQuery.tappable,
Mootools-mobile,
and tappable
Most of these libraries create a new event called tap. you can use the tap event similar to the click event. This jquery-mobile event handling might help.
$("#tappableElement").tap(function(){
// provide your implementation here. this is executed immediately without the 300ms delay.
});
Now, the second problem is with the handling of input focus.
There is a noticeable delay here also.
This can be solved by forcing focus on the element immediately for one of the touchstart or touchend events. This JQuery event handling might help.
$('#focusElement').bind('touchstart', function(e){
$(this).focus();
});
$('#focusElement').focus(function(e){
// do your work here.
});
You can do e.stopPropagation in 'touchstart' event handling to avoid propagation. But I would strongly advise against return false; or e.preventDefault as that would stop default functionality like copy/paste, selecting text etc.

Categories