Opened 6 years ago

Closed 6 years ago

Last modified 4 years ago

#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 Patrick Ruzand)

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)

test_FormLayout-scroll-with-native-controls.html (10.2 KB) - added by Adrian Vasiliu 6 years ago.
Variant of test_FormLayout.html with ScrollableView? and native controls

Download all attachments as: .zip

Change History (18)

comment:1 Changed 6 years ago by Patrick Ruzand

Description: modified (diff)

comment:2 Changed 6 years ago by Patrick Ruzand

Owner: set to Patrick Ruzand
Status: newassigned

comment:3 Changed 6 years ago by Patrick Ruzand

Description: modified (diff)

comment:4 Changed 6 years ago by Sebastien Pereira

Cc: Sebastien Pereira added

comment:5 Changed 6 years ago by Patrick Ruzand

Owner: changed from Patrick Ruzand to Adrian Vasiliu

comment:6 Changed 6 years ago by Adrian Vasiliu

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.

Last edited 6 years ago by Adrian Vasiliu (previous) (diff)

comment:7 Changed 6 years ago by Adrian Vasiliu

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 6 years ago by cjolif

Cc: cjolif added

comment:9 Changed 6 years ago by Nebulae

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 6 years ago by Adrian Vasiliu

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 6 years ago by Nebulae

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 6 years ago by Adrian Vasiliu

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.

Last edited 6 years ago by Adrian Vasiliu (previous) (diff)

comment:13 Changed 6 years ago by Patrick Ruzand

Milestone: tbd1.10
Priority: undecidedhigh

Changed 6 years ago by Adrian Vasiliu

Variant of test_FormLayout.html with ScrollableView? and native controls

comment:14 Changed 6 years ago by Adrian Vasiliu

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.

Last edited 6 years ago by Adrian Vasiliu (previous) (diff)

comment:15 Changed 6 years ago by Adrian Vasiliu <vasiliu@…>

Resolution: fixed
Status: assignedclosed

In 54bb4db1cc12bd55031c0bb22cfd51d110e78033/dojox:

Error: Processor CommitTicketReference failed
Unsupported version control system "git": Can't find an appropriate component, maybe the corresponding plugin was not enabled? 

comment:16 Changed 6 years ago by Adrian Vasiliu <vasiliu@…>

In b9e89116d06f47472afcdc7b6ce998c8313cf769/dojox:

Error: Processor CommitTicketReference failed
Unsupported version control system "git": Can't find an appropriate component, maybe the corresponding plugin was not enabled? 

comment:17 Changed 4 years ago by szerfas

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.

Note: See TracTickets for help on using tickets.