#17502 closed defect (fixed)
A scroll gesture started on a form widget activate both the ScrollableView scroll and the browser native scroll.
Reported by: | Patrick Ruzand | Owned by: | Adrian Vasiliu |
---|---|---|---|
Priority: | high | Milestone: | 1.10 |
Component: | DojoX Mobile | Version: | 1.9.1 |
Keywords: | Cc: | Sebastien Pereira, cjolif | |
Blocked By: | Blocking: |
Description (last modified by )
When a ScrollableView? contains a form widget like an input field, text area or button, and that the user starts a "scroll" gesture by touching such a dijit, both the ScrollableView? scroll and the browser native scroll are activated.
To reproduce, modify the dojox/mobile/tests/test_FormLayout.html to include a ScrollableView? instead of a View, and run it. Then, press on a textarea and start a vertical gesture to scroll the view.
Reproduce on iOS only (tested on iOS6 and 7)
Attachments (1)
Change History (18)
comment:1 Changed 9 years ago by
Description: | modified (diff) |
---|
comment:2 Changed 9 years ago by
Owner: | set to Patrick Ruzand |
---|---|
Status: | new → assigned |
comment:3 Changed 9 years ago by
Description: | modified (diff) |
---|
comment:4 Changed 9 years ago by
Cc: | Sebastien Pereira added |
---|
comment:5 Changed 9 years ago by
Owner: | changed from Patrick Ruzand to Adrian Vasiliu |
---|
comment:7 Changed 9 years ago by
preventDefaulting in touch.move is anyway not applicable inside the product because it would prevent interaction with a native (non-Dojo, pure HTML) slider (input of type range).
For now, the issue needs to be worked around at application level, using for instance the following code:
require([ "dojo/ready", "dojo/on", "dojo/touch", "dijit/registry" ], function(ready, on, touch, registry){ ready(function(){ // workaround to prevent browser scroll when starting the scroll // gesture on an input field. Should be done for each ScrollableView // containing elements of type SELECT, INPUT, TEXTAREA or BUTTON. var sv = registry.byId("sv"); on(sv, touch.move, function(e){ e.preventDefault(); }); }); });
comment:8 Changed 9 years ago by
Cc: | cjolif added |
---|
comment:9 Changed 9 years ago by
In my case, previous code was only half of the solution.
It's working fine to prevent whole window scrolling, but it still prevents using Android and iOS cursor drag-and-drop positioning feature (with the magnifier) : on multine textareas, dragging the cursor would trigger the scroll of the ScrollableView?, which automatically place cursor at the end of the input field text.
The following code fixes both issues. When a TextBox? or a TextArea? is focused and active, it stops touchmove event propagation to the ScrollableView? to keep default behavior.
Tested with dojo 1.9.1 / Android 4.4 / iOS 7.0.4
define([ "dojo/dom", "dojo/on", "dojo/ready", "dojo/sniff" ], function( dom, on, ready, has ){ // When DOM is ready ready(function(){ // If device has touch capabilities if(has("touch")) { // Get app main wrapper DOM node var appWrapper = dom.byId("myApp_Root"); // Workaround to prevent browser scroll when starting the scroll // gesture on an input field (SELECT, INPUT, TEXTAREA or BUTTON). // Done for each ScrollableView embedded in the app wrapper. on(appWrapper, ".mblScrollableView:touchmove", function(e){ e.preventDefault(); }); // Workaround to prevent ScrollableView scroll when input field // is focused and active, allowing Android and iOS cursor // drag-and-drop feature in dojox/mobile/TextBox on(appWrapper, ".mblScrollableView .mblTextBox:touchmove", function(e){ if(e.target == document.activeElement){ e.stopPropagation(); } }); // Workaround to prevent ScrollableView scroll when input field // is focused and active, allowing Android and iOS cursor // drag-and-drop feature in dojox/mobile/TextArea on(appWrapper, ".mblScrollableView .mblTextArea:touchmove", function(e){ if(e.target == document.activeElement){ e.stopPropagation(); } }); } }); });
comment:10 Changed 9 years ago by
Thanks Nebulae. You are right about the cursor issue in multi-line textareas. I've successfully tested the following "merge" of my initial solution and yours:
require([ "dojo/ready", "dojo/on", "dojo/_base/window", "dojo/touch", "dijit/registry" ], function(ready, on, win, touch, registry){ ready(function(){ // workaround to prevent browser scroll when starting the scroll // gesture on an input field. Should be done for each ScrollableView // containing elements of type SELECT, INPUT, TEXTAREA or BUTTON. var sv = registry.byId("sv"); // instance of ScrollableView/ScrollablePane on(sv, touch.move, function(e){ e.preventDefault(); if(e.target == win.doc.activeElement){ e.stopPropagation(); } }); }); });
That said, this still breaks the interaction of native HTML (non-Dojo) elements that rely on touchmove, such as the input of type range. That is, it remains a workaround working fine in most cases, but not applicable as a 1.9.x patch unfortunately.
comment:11 Changed 9 years ago by
Nice compacting Adrian, thanks. Actually, I have a ReferenceError? with "win" on Safari / iOS 7.0.4, should be replaced by "window".
Eventually, we can check for input type before managing event :
require([ "dojo/ready", "dojo/on", "dojo/_base/window", "dojo/touch", "dijit/registry" ], function(ready, on, win, touch, registry){ ready(function(){ // workaround to prevent browser scroll when starting the scroll // gesture on an input field. Should be done for each ScrollableView // containing elements of type SELECT, INPUT, TEXTAREA or BUTTON. var sv = registry.byId("sv"); // instance of ScrollableView/ScrollablePane on(sv, touch.move, function(e){ if(e.target.type != "range") { e.preventDefault(); if(e.target == window.doc.activeElement){ e.stopPropagation(); } } }); }); });
comment:12 Changed 9 years ago by
Nebulae, concerning the ReferenceError? for "win" - this shouldn't happen if you correctly imported in your test the "dojo/_base/window" module and you mapped it on the "win" variable of the require function. Anyway, both work in practice, it's just that the dojo/_base/window abstraction is our standard cross-browser way to access the document.
Concerning the check for the "range" input type: sure, but the HTML slider was only an example, and there's no generic way to test whether an element needs to handle touchmove events. That said, yes, the workaround code can be adapted to cope with various custom situations - that's precisely why it fits better as a workaround than as a patch... However, we might decide to patch despite the drawbacks. We'll see.
comment:13 Changed 8 years ago by
Milestone: | tbd → 1.10 |
---|---|
Priority: | undecided → high |
Changed 8 years ago by
Attachment: | test_FormLayout-scroll-with-native-controls.html added |
---|
Variant of test_FormLayout.html with ScrollableView? and native controls
comment:14 Changed 8 years ago by
Finally went for the following compromise: calling preventDefault() on touch.move only for the text widgets from dojox/mobile (TextBox?, TextArea?, ExpandingTextArea?, SearchBox?, ComboBox?) and their subclasses (technically, for any subclass of dijit/form/_TextBoxMixin - requiring only this module reduces to a minimum the amount of code additionally loaded by dojox/mobile/scrollable).
This avoids the "double scroll" for these widgets (which is the issue raised in the present ticket), while preserving the interaction based on touchmove for other elements such as a native slider (input with type=range) (thus avoids the risk of severe regressions for them).
Made the following PR: https://github.com/dojo/dojox/pull/78.
Tested on various Android, iOS, BB and WP8 devices without noticing any kind of regression (for instance, no perceived slowdown due to the additional checks).
Also attached here test_FormLayout-scroll-with-native-controls.html which allows to check the result for all these elements. I also added a native textarea in the test, for it the double scroll still exists - this is the price to pay for staying on the safe side, knowing that an app that would use a native textarea can solve the double scroll by either transforming it into a dojox/mobile/TextArea (preferred solution), or by adding workaround code from the outside as suggested in the previous comments. This is why the current solution would seem a good compromise to me.
comment:15 Changed 8 years ago by
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:17 Changed 7 years ago by
Not sure if best to open a new ticket but noticed this is still an issue if the TextBox? is close to the top or the bottom of the page.
To reproduce: 1) Use "test_FormLayout-scroll-with-native-controls.html" attached to this defect. 2) On iOS (or iOS Simulator) enter some characters in the last TextBox? (the TextBox? near the bottom) on the test page. 3) Attempt to move the cursor by holding (magnifier), move up enough to cause a scroll bar to appear and then finally release some where in the middle of the character sequence.
Result: Cursor will jump to the end of the text.
I noticed that if you see the scrollbar appear the cursor will jump to the end of the line. If you are careful with your scrolling and stay within the text then there is no problem. This does not occur or is far harder to reproduce on text fields landing in the middle of the view.
Also reproduced with 1.8 (and in 1.7 after changes to make the test compatible). It is not specific to iOS, although the misbehavior on Android is a bit different.
The cause of this issue is simple: dojox/mobile/scrollable calls e.preventDefault() on dojo/touch#press *except* if the target is an input field. Hence, when starting the scrolling over the input field, the browser scroll is not prevented. Now, the reason why we don't preventDefault for input fields is that doing so would forbid the keyboard from showing up.
I have tested the following quick solution: instead of doing a selective preventDefault() on touch.press, doing it always (including for input fields) on dojo/touch#move. This improves the situation on both iOS and Android. However, scrolling very slowly starting from an input field pops the keyboard. Globally I think this is still preferable to the current behavior, but I try to find a solution for this last glitch too - it seems due to the synthetic clicks generated by dojo/touch.