#15878 closed defect (fixed)
dojo/touch click event normalization and fixes
Reported by: | Damien Mandrioli | Owned by: | Eric Durocher |
---|---|---|---|
Priority: | undecided | Milestone: | 1.9 |
Component: | General | Version: | 1.8.0 |
Keywords: | Cc: | cjolif, bill | |
Blocked By: | Blocking: |
Description (last modified by )
When you click on a ListItem contained in a ScrollableView, the click event is duplicated.
This affects only Android 4.1 and is related to http://code.google.com/p/android/issues/detail?id=36116
Workaround: listen for touchEnd instead of click event or use a View instead of a ScrollableView.
Attachments (4)
Change History (47)
Changed 10 years ago by
Attachment: | 15878.patch added |
---|
comment:1 Changed 10 years ago by
Cc: | cjolif added |
---|
The ScrollableView fires synthetic click events because it prevents/stops touchstart/move events (to disable native browser scrolling), and in that case the OS normally does not fire click events on touchend. However, this is not true in Android 4.1: the click event is still fired even though touchstart/move events are stopped. This explains why we see duplicate click events. The solution is to not fire click events again on Android 4.1.
This will have to be tested again on future Android versions to see if the behavior changes again...
comment:4 Changed 10 years ago by
Milestone: | tbd → 1.8.1 |
---|
comment:5 Changed 9 years ago by
Milestone: | 1.8.1 |
---|---|
Resolution: | fixed |
Status: | closed → reopened |
Testing the Android version (>= 4.1) does not seem reliable enough, as the click event is apparently not sent on Android 4.1 devices using the Chrome browser (Galaxy Note II). So, I am reopening this and looking for a more general solution not based on version sniffing.
comment:6 Changed 9 years ago by
This other problem seems similar: #16676. Not really a duplicate since it is not in the same module, but the resolution should be similar.
comment:7 Changed 9 years ago by
Cc: | bill added |
---|
OK, I'd like to propose this new patch as a browser independent solution.
The idea is the following: instead of trying to guess which browser will / will not send a click after a preventDefault() on touch events, we always generate click events ourselves in dojo/touch, and we always stop native click events sent by the browser.
This should solve several different problems:
- this ticket about dojox/mobile/scrollable, and the similar ticket on mobile Switch: #15922 (with the patch, we can just remove the ugly code that generated the clicks in these widgets)
- a similar ticket on dojox/gesture: #16676
- and also the "click delay" problem: #16720
The patch can probably be enhanced, for example we could probably avoid adding new listeners on the document for touchmove and touchend. I added different listeners because we need to always be notified of touchmove/end events, even if the event was stopped in a listener, and the existing listeners in dojo/touch do not use addEventListener(..., true). The existing listeners could probably be changed to use addEventListener(..., true), thus removing the need for the new listeners. Bill?
Also, not sure about the move threshold (40 currently, this seems approximately what iOS does...)
comment:8 Changed 9 years ago by
I was also thinking about the click delay, which some people consider a problem. You listed it as #16720 but there's also a lot of prior discussion at http://thread.gmane.org/gmane.comp.web.dojo.devel/17903 and #15110. My key point from #15110 is that the delay is by design from Apple to enable page zoom via double click, so we want to be cautious if we are intentionally breaking that behavior.
Your proposed patch is similar to code that's already in dijit/a11yclick.js. The main difference of your code, compared to the code in a11yclick, is that your code is applying this behavior to every node on the page, not just nodes with a11yclick listeners on them.
Your code is better for performance since it just sets up a few listeners on the page rather than listeners on every node, and it possibly fixes problems trying to use event delegation with dijit/a11yclick (ex: putting a dijit/a11yclick handler on a Toolbar instead of the individual buttons). Your code is more dangerous though; developers might not appreciate how merely loading dojo/touch causes side effects on their whole page. A compromise would be to use your design, but only enable that behavior for nodes that have a property (ex: dojoClick) and/or attribute (ex: data-dojo-click-node) or class name set. Perhaps also check if it's set on an ancestor of the node. That's similar to _CssStateMixin's technique. Not sure what's best.
Like the a11yclick code, your code has a bug where zooming the page via doubleclick causes an unexpected click event. Except your bug is presumably worse because it affects doubleclicking anywhere on the page, not just doubleclicking nodes with dijit/a11yclick handlers. Not an issue for pages where zoom is disabled, but it could be an issue for other pages.
Also, you shouldn't be using dojo/_base/event, it's effectively deprecated. Pluswhich, dojo/_base/event::stop() calls evt.stopPropagation() but presumably you want to call evtlstopImmediatePropagation(), in case the clicked node itself has a "click" listener registered.
comment:9 Changed 9 years ago by
PS: The idea behind a synthetic event like dijit/a11yclick (which perhaps should just be called dijit/click) is that app code call on(node, a11yclick, ...)
instead of on(node, "click", ...)
. If code follows that rule, the on.emit() call doesn't have to emit "click", it could emit "dojoclick" etc., similar to how the other code in dojo/touch works. That would be more straightforward, albeit users would need to be trained to do on(node, a11yclick, ...)
.
comment:10 Changed 9 years ago by
Yes I thought of a separate dojoclick event too (this could be exposed as touch.click), but did not do it for the same 2 reasons you mention...
About your previous comments, I agree this may be too intrusive and it would probably be safer to explicitly enable this on a specific node/hierarchy only.
comment:11 Changed 9 years ago by
New version of the patch which
- also generates clicks on IE10 (where the delay is noticeable too)
- generates clicks only for nodes that have the dojoClick property set to true (set by dojox/mobile to the whole document by default).
- replaces similar code in dijit/a11yclick by a dojo/touch require
Note, the old dijit/a11yclick code was not strictly equivalent, since it used a timer to wait for a potential native click fired by the browser. To decide if we really want to do this.
comment:12 Changed 9 years ago by
New version of patch where dojoClick is set only on some dojox/mobile widgets where the native behavior is problematic (Buttons, scrollable, Switch). Also added a way to customize the move threshold per node.
Changed 9 years ago by
Attachment: | touch_click.patch added |
---|
On touch-enabled devices, generate clicks in dojo/touch instead on relying on the browser, to avoid inconsistent behaviors - Eric Durocher (IBM, CCLA)
comment:13 Changed 9 years ago by
Yet another version: reverted to setting dojoClick on the whole body, to get consistent click behavior on all mobile widgets.
Changed 9 years ago by
Attachment: | touch_click_2.patch added |
---|
Fix some regressions on dijit widgets on mobile - Eric Durocher (IBM, CCLA)
comment:18 Changed 9 years ago by
Component: | DojoX Mobile → General |
---|---|
Description: | modified (diff) |
Milestone: | → 1.9 |
Summary: | ScrollableView: ListItem click event is duplicated on Android 4.1 → dojo/touch click event normalization and fixes |
Changing title since this is mainly a ticket about changes to dojo/touch now (although it does fix the ScrollableView problem).
comment:28 Changed 9 years ago by
Synthetic clicks are sent even on disabled elements, patch attached to #16866 : http://bugs.dojotoolkit.org/attachment/ticket/16866/touch_click_disabled.patch
Changed 9 years ago by
Attachment: | click_backtrack.patch added |
---|
Backtrack use of dojoClick to replace synthetic clicks in mobile - Eric Durocher (IBM, CCLA)
comment:30 Changed 9 years ago by
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Having more problems with this approach of generating faux click events, so will try another approach of just generating synthetic events with a different name.
Eric reports that: In dojox/mobile/tests/test_FormControls.html : if I click *exactly* on a RadioButton's check box it works as expected, but if I click on the label, other radios are not deselected, apparently because the widget does not receive the click...
From me: On iPad in dojox/mobile/tests/test_FormControls.html I'm seeing the checkbox toggle instantly (as we want) but then toggling again 300ms later, back to the original value. By my debugging, apparently what's happening is:
- the user clicks <label for="cbox">Click me</label>
- we emit synth click on that <label>
- the click on <label> generates a click event on the associated checkbox <input type="checkbox" ...>. we sort-of block that native event, but not really, due to [30858], so the checkbox toggles.
- 300ms later, the real click event on the <label> comes. again we try to suppress it, but we don't, due to [30858], so the checkbox gets another click event, and toggles again.
If I revert [30858] then clicking the label just doesn't do anything.
It seems like we are between a rock and a hard place... [30858] was needed on android because the synthetic click event wouldn't perform the browser default action. But as I worried, it causes repercussions [on iOS]. The other complication is of course that it's hard to suppress the click event from the touchstart/touchend without accidentally suppressing other click events.
comment:31 Changed 9 years ago by
Also, the stopNativeEvents() code from [30828] stops phantom click events from appearing on items behind a Menu (that is closed by the click event). However, it's problematic for clicking small things. On dijit/tests/form/mobile.html (and other tests) clicking on the checkboxes or their labels often generates touchstart and touchend events on nearby nodes, such as the containing <fieldset> or it's <legend>. The checkbox or label still gets a click event though. Somehow the browser adjusts for it.
comment:41 Changed 9 years ago by
Coming back to ticket:15878#comment:5 and the specific problem on Galaxy Note 2: just tested with 1.9, all works OK now on that device. It is true, though, that if we set document.dojoClick = false; to disable the new 1.9 click machinery, Switch widgets for example do not receive click events, so the legacy code that generates click conditionally on some devices seems to fail on Galaxy Note 2. I don't think it is worth fixing as long as the default (new) dojoClick code works fine.
Do not fire synthetic click events in ScrollableView for Android >= 4.1 - Eric Durocher (IBM, CCLA)