Re: beforescroll and using DOM events for extensibility

On Fri, Oct 3, 2014 at 10:00 AM, Olli Pettay <olli@pettay.fi> wrote:

> On 10/03/2014 04:14 PM, Rick Byers wrote:
>
>> Jonas and Anne raised some good concerns (https://www.w3.org/Bugs/
>> Public/show_bug.cgi?id=12230) with the way we're thinking of using DOM
>> events for
>> beforescroll (https://docs.google.com/a/chromium.org/document/d/
>> 1oEVWIVdMZ2OlVZMvcZZ3IgaT6RAUNSKAzpzb9AlVeLw/edit#).  Moving the
>> discussion here
>> (independent from the other thread about the performance tradeoffs of
>> enabling JS customization of scrolling).
>>
>>  > Jonas Sicking:
>>  > >
>>  > > Enabling pages to trigger actions that are triggered by other larger
>> features is indeed usually a good idea,
>>  > > for the reasons enumerated in the extensible web manifest.
>>  > >
>>  > > However enabling pages to trigger those actions by creating an event
>> and calling dispatchEvent is very
>>  > > awkward.
>>  > >
>>  > > Normally JS APIs are built using functions. Like
>> "window.doStuff(5)". While possible to use a pattern like
>>  > >
>>  > > event = new CustomEvent("stuff");
>>  > > event.detail = 5;
>>  > > window.dispatchEvent(event);
>>  > >
>>  > > it is quite awkward.
>>  > > Instead define a JS function, and then define that this function is
>> called as default action in response to some particular event.
>>  > >
>>  > Anne van Kesteren:
>>  >
>>  > It's not just awkward, it doesn't match the prescribed events model.
>> Which is that dispatchEvent() returns
>>  > a boolean and based on that you might initiate a default > action (or
>> not). So the polyfill would always be
>>  > along the lines of
>>  >
>>  >   var ev = ...
>>  >  if(obj.dispatchEvent(ev))
>>  >    obj.defaultAction()
>>  >
>>  > That just dispatching the event invokes obj.defaultAction() breaks the
>> event model and is a
>>  > longstanding bug for some events in browsers (this bug, indeed).
>> This makes perfect sense when the browser behavior for an event should
>> come strictly after any behavior on the page (i.e. it's really the final
>> "default action" - coupled with the thing that generated the event in the
>> first place).  But that strict "JS before browser" pattern isn't always
>> consistent with the extensible web ideas of some browser behavior being
>> built on the same primitives available to the application.  For full
>> composition of built-in and JS behavior, there may be cases where some JS
>> behavior happens only after some built-in behavior has been given a chance
>> to run.
>>
>> In particular, for scrolling, consider the case of a scrollable DIV
>> nested inside a scrollable document.  We want to allow the scrolling
>> behavior of
>> the DIV to either be supplied by the browser or by some JS library.  In
>> either case, when the DIV has reached its scroll extent, further scrolling
>> chains up to the document.
>>
> So something like element.requestScroll(x, y); could be exposed to the web
> pages and it would behave as if user had initiated the scroll
> (at least in most cases. Maybe we'll need something like trusted scroll
> which is coming from user).


We already have that - Element.scrollBy. What we don't have is a way to
customize how scroll inputs (deltas) are distributed to the outputs (which
element(s) will respond).


>> In our efforts to make this behavior extensible, it seemed most natural
>> to model the scroll request as a DOM event,
>>
> Events aren't requests. Events tell that something, well event, will
> happen or just happened.
>
> (dispatching click event is a special legacy case, but I don't think we
> can ever get rid of it.)


I like to think of customizing scrolling as exposing the user's request
("intention": http://w3c.github.io/editing-explainer/commands-explainer.html).
But we could equally think of this as an input-type-agnostic gesture.  So
instead of "beforescroll", call it "draggesture" (but be aware that there
are keyboard accelerators for triggering common drag gestures).

 and the chaining as bubbling of
>> that event.  But for proper composition, this implies that browser
>> behavior must act as just an event handler - not a default action that
>> occurs
>> strictly after event processing.
>>
>> Personally I really like the idea of the browser relying on the same
>> addEventListener mechanism available to applications in order to trigger
>> certain
>> behavior.
>>
> What same addEventListener mechanism? web pages add listeners in such way
> that stopPropagation affects to them.
> Browser implementation don't care about stopPropagation.


I agree that's how implementations behave today.  But I'm suggesting it's
at odds with the ideas of the extensible web to require the browser
implementation to always adhere to such a hard distinction between what's
"built-in" and whats provided in JS by some framework.  Eg. if someone
decides we should standardize a new input element type, following the
extensible web principles they would "develop, design, and test" it in
HTML/CSS/JS first (possibly as a web component).  So they way it responds
to events would be affected by stopPropagation and not by preventDefault.
Then we decide to implement that component in the browser (maybe even
shipping the same JS - see for example
http://www.chromium.org/blink/blink-in-js).  Why should the behavior of
stopPropagation and preventDefault need to change?  Why do other components
of the page even need to care whether the component they're using is
implemented by the browser or by some JS library?  Do other successful UI
frameworks have such a similar hard separation between what is and isn't
"built-in"?

For customizing scrolling, we'd like to allow developers to "understand and
replicate" the behavior of an overflow:scroll DIV. Maybe it's OK to have
differences like the difference between stopPropagation and
preventDefault.  But we still need some way to compose browser behavior
with JS in a way where the JS runs only AFTER the browser behavior has had
a chance to run.

In particular, this is one of the big challenges we hit when trying to
build a great pull-to-refresh component.  How do you customize scrolling
(or general input handling) behavior for the document, without affecting
the behavior of nested scrollable elements (especially iframes)?  We don't
think it's acceptable to say that customizing document scrolling requires
you to also customize the scrolling of every nested scrollable element.

Has this class of problems not been an issue for the web platform in the
past?  Have we really never had to support input customization of a
container which may have children whose input handling we don't want to
interfere with?  This seems like a pretty important class of scenarios in
other UI frameworks designed for composition.  Eg. In Android, I can
implement a subclass of ScrollView which overrides onTouchEvent but my
child views (even "built in" ones) still get a chance to declare they will
handle the touch before it's passed to my handler.  iOS has a similar
hitTest:withEvent mechanism.

I believe it's urgent that we similarly empower the web somehow, but I'd
love to hear other suggestions if the beforescroll event handling model is
too weird.

It ensures natural / seamless composition between JS and native behavior,
>> similar to what you see in traditional OO UI frameworks (where
>> the framework relies on the same OO design principles for composition as
>> are available to the application for extensibility).  We actually already
>> have a number of places that do this in blink and webkit (most commonly
>> in shadowDOM components, eg. for form elements).
>>
> Sounds like a bug in shadow DOM if it needs to rely on event dispatching
> to trigger default action, and it is against the current spec.
> There is a reason why XBL1 has a way to add listeners to system event
> group (aka, the group which is for browser implementation only for dealing
> default actions).
>

I checked a couple of major cases I thought behaved this way today and
found I was wrong - they do handle input as a default action.  There appear
to be a couple edge cases that behave as I described, but I agree they're
probably bugs.  Sorry for the confusion.


>> Thoughts?
>>     Rick
>>
>>
>>
>

Received on Friday, 3 October 2014 15:27:34 UTC