You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@wicket.apache.org by Martin Grigorov <mg...@apache.org> on 2013/07/11 10:59:20 UTC

Ajax behavior for JavaScript event delegation

Hi,

At https://github.com/apache/wicket/compare/event-delegating-behavior you
may see the diff between master and event-delegating-behavior branches.

The latter provides a new AjaxEventBehavior (AEB) - EventDelegatingBehavior
(EDB), that suppresses the JS event binding for all AjaxEventBehaviors for
a given event type (click, submit, change, ...) in the children components
of the host component of EDB.

How EDB works:

- until now AjaxEventBehavior#renderHead() renders ondomready header item
with JS snippet like:
Wicket.Ajax.ajax(attributesObject);
In the new branch there is a check if some parent has EDB for the event
type of this AEB, and if there is such then the AEB "donates" its
attributes to the EDB.

- EventDelegatingBehavior#getCallbackScript() renders :
Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
childrenAttrsMap);

- when a delegated component fires its event (e.g. the user clicks on an
AjaxLink) the event is handled by EDB's event handler. It extracts the
markupId of the inner HTML element and fires Wicket.Ajax.Call with the
specific attributes for the extracted inner element.

Pros:

- simple to use - just add EDB to a container component around your Ajax
heavy component (e.g. repeater with many Ajax behaviors). See the demo app
at https://issues.apache.org/jira/browse/WICKET-5267

-  faster JS execution
-- faster execution of the domready handler because there is just one
binding instead of N
-- faster reaction because the browser finds the event handler much faster.
I wasn't able to prove this with numbers because there is no way to detect
the 'start time', i.e. when the user makes the action. With JS the earliest
point is when the browser has already looked up the event handler.
Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
reference that it is faster are the articles in the web and a use case in
our application.

Cons:

- AEB#renderHead() needs to check whether there is EDB up in the hierarchy
to be able to decide what to do.
This is ugly, I agree. But I see no other solution that will preserve the
transparent usage of something like EDB and will not require a major
rewrite of user applications to be able to use event delegation.
-- there are some optimizations to lower the impact of the new checks:
--- a new setting (IAjaxSettings#useEventDelegation) - a global property
that prevents visiting the parent components and their behaviors for all
apps which do not use EDB
--- when EDB is bound it registers a metadata for its event type in the
page instance. This prevents visiting all behaviors of all parent components


I have no more ideas how to further optimize it.

Any feedback is welcome! Even if you have a completely different idea how
to implement this functionality.

Thanks for reading!

Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
On Wed, Jul 31, 2013 at 3:04 PM, Martin Grigorov <mg...@apache.org>wrote:

> Hi Sebastien,
>
>
> On Wed, Jul 31, 2013 at 2:37 PM, Sebastien <se...@gmail.com> wrote:
>
>> Hi,
>>
>> I agree with Igor. Just have to take care that binding to document is not
>> a
>> default behavior for all bound events, and that it should be use with
>> care.
>>
>
> I think Igor said the opposite - bind on document with child selector:
> $(document).on('click', '[data-w-click]', handler);
>
>
>> (IMHO, the javadoc should be enough)
>> Martin, I unfortunately did not had the chance to have a look at your code
>> now... :s But, if it not the case, if think EventDelegatingBehavior should
>> have a constructor that take a component (the datatable for instance) as
>> an
>>
>
> Behavior#bind(Component) gives you a reference to the component. No need
> to pass it as ctor parameter.
>
>
>> argument so the event can be bound it it instead of the document (by
>> getting its markupid as the selector)...
>
>
>> [quote]
>> Here is another important tips "*Attaching many delegated event handlers
>> near the top of the document tree can degrade performance*". For best
>> performance, attach delegated events at a document location as close as
>> possible to the target elements as done in above jQuery code
>> [/quote]
>>
>>
>> http://jquerybyexample.blogspot.com/2013/04/direct-vs-delegated-events-with-jquery-on-method.html
>
>
> According to this article it is better to bind on the table than on the
> document.
> At least this is how I read it.
>
>
>>
>>
>>
>> I think events delegation would be a great addition anyhow...
>>
>> Best regards,
>> Sebastien.
>>
>>
> From the discussion so far I see these two issues which need to be solved
> before starting make changes in the code:
>
> 1) it must be possible to use event delegation on "the table", not only on
> the document
> 2) if the ajax attributes are stored in data-w-eventName attribute on the
> element then we need to figure out where to store the Ajax call listeners'
> functions
>

<input type="text" wicket:id="ac" size="50" value="" name="ac" id="ac2"
autocomplete="off"
data-w-change="{&quot;f&quot;:&quot;form1&quot;,&quot;u&quot;:&quot;./autocomplete?1-1.IBehaviorListener.1-form-ac&quot;,&quot;e&quot;:&quot;change&quot;,&quot;m&quot;:&quot;POST&quot;}"/>

At the moment the value for data-w-eventName It is not very readable...


>
>
> For 1) I see it as a new Ajax request attribute - bindOnComponent = true
> If the attribute is 'false' then the event will be bound on the document
> instead and will use event delegation.
> This way all <td>s can return false, and the <table> can return true to
> handle the bubbling event (and stop it, because otherwise it will be
> handled twice when the event reaches the document)
>
> For 2) We can introduce global registry (e.g.
> Wicket.Ajax.Handlers[markupId][handlerType] =>
> Wicket.Ajax.Handlers["someId12"]["success"] = function(...) {...}).
> So the "global" handler (bound on the table or on the document) can use
> the specific Ajax call listeners for the clicked element with attribute
> 'data-w-eventName'
>
> WDYT ?
>
>
>
>>
>>
>> On Tue, Jul 30, 2013 at 7:07 PM, Igor Vaynberg <igor.vaynberg@gmail.com
>> >wrote:
>>
>> > binding on document is fine, you just have to make sure your code is
>> fast
>> > in case you are binding to things like mousemove.
>> >
>> > -igor
>> >
>> >
>> > On Tue, Jul 30, 2013 at 1:31 AM, Martin Grigorov <mgrigorov@apache.org
>> > >wrote:
>> >
>> > > On Fri, Jul 12, 2013 at 5:21 PM, Igor Vaynberg <
>> igor.vaynberg@gmail.com
>> > > >wrote:
>> > >
>> > > > On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <
>> mgrigorov@apache.org
>> > >
>> > > > wrote:
>> > > > > On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <
>> > > igor.vaynberg@gmail.com
>> > > > >wrote:
>> > > > >
>> > > > >> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <
>> > > mgrigorov@apache.org>
>> > > > >> wrote:
>> > > > >> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net>
>> > > wrote:
>> > > > >> >
>> > > > >> >> Hi,
>> > > > >> >>
>> > > > >> >>
>> > > > >> >> >The idea with plain JS solution I cannot visualize in my head
>> > yet.
>> > > > >> >>
>> > > > >> >> EventDelegatingBehavior is just a collector of JavaScript
>> > snippets.
>> > > > The
>> > > > >> >> actual magic runs in the browser: a custom bubbling of events
>> and
>> > > > >> >> delegation to the actual behavior.
>> > > > >> >> It should be possible to do this plain with JavaScript:
>> > > > >> >>
>> > > > >> >>   public class DelegatingAjax implements IAjax {
>> > > > >> >>
>> > > > >> >>     public ajax(IHeaderResponse response, Component component,
>> > > > >> >> AjaxRequestAttributes attributes) {
>> > > > >> >>       CharSequence ajaxAttributes =
>> > > renderAjaxAttributes(**component,
>> > > > >> >> attributes);
>> > > > >> >>
>> > > > >> >>
>> > > > >>
>> > > >
>> > >
>> >
>> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
>> > > > >> >> + ajaxAttributes + ");");
>> > > > >> >>     }
>> > > > >> >>   }
>> > > > >> >>
>> > > > >> >> This would be page-global though.
>> > > > >> >
>> > > > >> >
>> > > > >> > This is an important detail!
>> > > > >> > I'll consult with my frontend colleagues but so far I don't see
>> > > > problems.
>> > > > >> >
>> > > > >> > For every delegated component we can set special CSS class,
>> e.g.
>> > > > >> > 'wicket-delegated'.
>> > > > >> > The binding will be: $(document).on('click',
>> '.wicket-delegated',
>> > > > >> > function(event) {....})
>> > > > >> > i.e. we will take advantage of jQuery delegation/live support.
>> > > > >> > This way even newly added items in the repeaters will be
>> > > automatically
>> > > > >> > supported.
>> > > > >>
>> > > > >>
>> > > > >> this is partially on the right track, but there are still some
>> > > > >> optimization that can be made.
>> > > > >>
>> > > > >> first, the ajax attributes need to be moved into a data attribute
>> > that
>> > > > >> is written out on the tag. the final output of attaching a
>> onclick
>> > > > >> ajax behavior to a tag should end up looking like this:
>> > > > >>
>> > > > >> <a wicket:id="ajaxlink"
>> > > > >> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
>> > > > >>
>> > > > >> (we will need to figure out how to encode ajax attributes into a
>> > > string)
>> > > > >>
>> > > > >
>> > > > > example:
>> > > > > <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
>> > > > >
>> > > > > $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
>> > > > >
>> > > > > This works for valid JSON, but it doesn't for the enhancement we
>> use
>> > -
>> > > > the
>> > > > > functions for the call listeners.
>> > > >
>> > > > i did say we need to figure out a way to encode it right above the
>> > > example
>> > > > :)
>> > > >
>> > > > >> then you can have the one global listener:
>> > > > >>
>> > > > >> $(document).on("click", function(e) {
>> > > > >>
>> > > > >
>> > > > > The problem here is that using 'document' will make the things
>> > actually
>> > > > > slower.
>> > > > > We need to find a simple way to be able to bind on a parent
>> > component.
>> > > > > In Sven's example - a table with many cells the most appropriate
>> > > element
>> > > > is
>> > > > > the <table> itself.
>> > > >
>> > > > umm, why does it make things slower exactly? this has virtually no
>> > > > overhead, events bubble up anyways...so where does the slowness come
>> > > > from?
>> > > >
>> > >
>> > > All the talks about the deprecation of jQuery#live() say that binding
>> on
>> > > the document is not a good idea (performance wise).
>> > >
>> > > If it is not possible to bind on a context element then I see no much
>> > > benefit.
>> > >
>> > >
>> > > >
>> > > > -igor
>> > > >
>> > > > >
>> > > > > In event-delegating-behavior branch I need to traverse the parent
>> > > > > components and their behaviors to be able to find the appropriate
>> > > parent.
>> > > > > So we win some performance in JS execution but lose some in Java
>> :-/
>> > > > >
>> > > > >    var element=$(this), attrs=element.attr("data-w-click");
>> > > > >>    if (attrs&&!e.handledByWicket)
>> > > > >>        Wicket.Ajax.call(attrs);
>> > > > >>        e.handledByWicket=true; // if there are more handlers
>> above,
>> > do
>> > > > >> not double process the event - read below
>> > > > >>    }
>> > > > >> }
>> > > > >>
>> > > > >> the advantage here is that we only have one javascript listener
>> that
>> > > > >> needs to be registered.
>> > > > >>
>> > > > >> however, there are a few disadvantages:
>> > > > >> * event propagation options wont work anymore, because the event
>> has
>> > > > >> to propagate all the way to the document in order to trigger.
>> > > > >> * some libraries block events. for example if there is a panel
>> with
>> > an
>> > > > >> ajax link inside a third party modal window. the modal window lib
>> > may
>> > > > >> prevent any clicks from propagating out of itself, which means
>> the
>> > > > >> handler on the document will never see them.
>> > > > >>
>> > > > >> we can sort of solve this by having a behavior that would write
>> out
>> > > > >> the listener above, but attached to the component not the
>> document.
>> > > > >>
>> > > > >> that way, if we look at my example with the panel inside the
>> modal,
>> > > > >> the user can add this behavior to the panel that will be in the
>> > modal
>> > > > >> and still be able to capture the event.
>> > > > >>
>> > > > >> this does, however, make troubleshooting more difficult. why
>> didnt
>> > my
>> > > > >> ajax event trigger? you will have to be a lot more aware about
>> what
>> > > > >> javascript you have in the dom.
>> > > > >
>> > > > >
>> > > > >> i think a short term goal might be to move the ajax attributes
>> into
>> > a
>> > > > >> dom attribute and change our ajax code to simply say
>> > > > >> Wicket.Ajax.bind("click", "component234");
>> > > > >>
>> > > > >
>> > > > > see above (valid JSON)
>> > > > >
>> > > > > we can enrich the DOM:
>> > > > > <a ... onsuccess="someScript">
>> > > > > but I think this is a step back to Wicket 1.5 days (ajax
>> decorators
>> > on
>> > > > > strings, etc.)
>> > > > >
>> > > > >
>> > > > >>
>> > > > >> this will register the listener like above on the element
>> directly.
>> > so
>> > > > >> no delegation yet but cleaner javascript/html. also the browser
>> > doesnt
>> > > > >> have to parse as much javascript, so it will be a bit speedier.
>> > > > >>
>> > > > >> potentially we can collect ids to further optimize js size:
>> > > > >> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
>> > > > >>
>> > > > >> -igor
>> > > > >>
>> > > > >>
>> > > > >> >
>> > > > >> >
>> > > > >> >>
>> > > > >> >>
>> > > > >> >> Sven
>> > > > >> >>
>> > > > >> >>
>> > > > >> >>
>> > > > >> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>> > > > >> >>
>> > > > >> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <
>> nbpratt@gmail.com>
>> > > > wrote:
>> > > > >> >>>
>> > > > >> >>>  I think this is great - we have some tables now with a ton
>> of
>> > JS
>> > > > >> events
>> > > > >> >>>> on
>> > > > >> >>>> the child elements.  Just to clarify, will this make the
>> > rendered
>> > > > page
>> > > > >> >>>> smaller since there will only be a single JS handler for the
>> > > event
>> > > > for
>> > > > >> >>>> the
>> > > > >> >>>> container rather than N JS handlers?
>> > > > >> >>>>
>> > > > >> >>>>  At the moment all attributes for an inner element are
>> > preserved.
>> > > > >> >>> 'e' (the event name), 'c' (the component markup id), pd
>> (prevent
>> > > > >> default),
>> > > > >> >>> sp (stop propagation) can be removed because they are not
>> really
>> > > > used.
>> > > > >> >>> But every inner element can have its own call listeners, form
>> > > > >> submitters
>> > > > >> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I
>> think
>> > > > they
>> > > > >> have
>> > > > >> >>> to be preserved.
>> > > > >> >>> If you look in #updateAjaxAttributes() for your ajax
>> behaviors
>> > in
>> > > > your
>> > > > >> >>> table cells you will probably notice that they have their own
>> > > > >> attributes.
>> > > > >> >>>
>> > > > >> >>>
>> > > > >> >>>  Making it switchable (I think how Sven suggested) would be
>> an
>> > > > >> >>>> improvement -
>> > > > >> >>>> we could leave it off by default, but provide a simple
>> switch
>> > on
>> > > a
>> > > > >> >>>> per-container (or per-app) basis that would allow the dev to
>> > > > choose.
>> > > > >> >>>>
>> > > > >> >>>>  Yes, it looks as an improvement.
>> > > > >> >>> Moving the current code to such implementation is easy.
>> > > > >> >>> The idea with plain JS solution I cannot visualize in my head
>> > yet.
>> > > > >> >>>
>> > > > >> >>>
>> > > > >> >>>  Regards
>> > > > >> >>>>
>> > > > >> >>>> Nick
>> > > > >> >>>>
>> > > > >> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
>> > > > >> mgrigorov@apache.org
>> > > > >> >>>>
>> > > > >> >>>>> wrote:
>> > > > >> >>>>> Hi,
>> > > > >> >>>>>
>> > > > >> >>>>> At https://github.com/apache/**wicket/compare/event-**
>> > > > >> >>>>> delegating-behavioryou<
>> > > > >>
>> > https://github.com/apache/wicket/compare/event-delegating-behavioryou
>> > > >
>> > > > >> >>>>> may see the diff between master and
>> event-delegating-behavior
>> > > > >> branches.
>> > > > >> >>>>>
>> > > > >> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
>> > > > >> >>>>>
>> > > > >> >>>> EventDelegatingBehavior
>> > > > >> >>>>
>> > > > >> >>>>> (EDB), that suppresses the JS event binding for all
>> > > > >> AjaxEventBehaviors
>> > > > >> >>>>>
>> > > > >> >>>> for
>> > > > >> >>>>
>> > > > >> >>>>> a given event type (click, submit, change, ...) in the
>> > children
>> > > > >> >>>>>
>> > > > >> >>>> components
>> > > > >> >>>>
>> > > > >> >>>>> of the host component of EDB.
>> > > > >> >>>>>
>> > > > >> >>>>> How EDB works:
>> > > > >> >>>>>
>> > > > >> >>>>> - until now AjaxEventBehavior#renderHead() renders
>> ondomready
>> > > > header
>> > > > >> >>>>> item
>> > > > >> >>>>> with JS snippet like:
>> > > > >> >>>>> Wicket.Ajax.ajax(**attributesObject);
>> > > > >> >>>>> In the new branch there is a check if some parent has EDB
>> for
>> > > the
>> > > > >> event
>> > > > >> >>>>> type of this AEB, and if there is such then the AEB
>> "donates"
>> > > its
>> > > > >> >>>>> attributes to the EDB.
>> > > > >> >>>>>
>> > > > >> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>> > > > >> >>>>> Wicket.Event.delegate('**edbComponentMarkupId',
>> 'eventType',
>> > > > >> >>>>> edbAttributes,
>> > > > >> >>>>> childrenAttrsMap);
>> > > > >> >>>>>
>> > > > >> >>>>> - when a delegated component fires its event (e.g. the user
>> > > clicks
>> > > > >> on an
>> > > > >> >>>>> AjaxLink) the event is handled by EDB's event handler. It
>> > > extracts
>> > > > >> the
>> > > > >> >>>>> markupId of the inner HTML element and fires
>> Wicket.Ajax.Call
>> > > with
>> > > > >> the
>> > > > >> >>>>> specific attributes for the extracted inner element.
>> > > > >> >>>>>
>> > > > >> >>>>> Pros:
>> > > > >> >>>>>
>> > > > >> >>>>> - simple to use - just add EDB to a container component
>> around
>> > > > your
>> > > > >> Ajax
>> > > > >> >>>>> heavy component (e.g. repeater with many Ajax behaviors).
>> See
>> > > the
>> > > > >> demo
>> > > > >> >>>>>
>> > > > >> >>>> app
>> > > > >> >>>>
>> > > > >> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
>> > > > >> https://issues.apache.org/jira/browse/WICKET-5267>
>> > > > >> >>>>>
>> > > > >> >>>>> -  faster JS execution
>> > > > >> >>>>> -- faster execution of the domready handler because there
>> is
>> > > just
>> > > > one
>> > > > >> >>>>> binding instead of N
>> > > > >> >>>>> -- faster reaction because the browser finds the event
>> handler
>> > > > much
>> > > > >> >>>>>
>> > > > >> >>>> faster.
>> > > > >> >>>>
>> > > > >> >>>>> I wasn't able to prove this with numbers because there is
>> no
>> > way
>> > > > to
>> > > > >> >>>>>
>> > > > >> >>>> detect
>> > > > >> >>>>
>> > > > >> >>>>> the 'start time', i.e. when the user makes the action.
>> With JS
>> > > the
>> > > > >> >>>>>
>> > > > >> >>>> earliest
>> > > > >> >>>>
>> > > > >> >>>>> point is when the browser has already looked up the event
>> > > handler.
>> > > > >> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't
>> help
>> > > too.
>> > > > So
>> > > > >> my
>> > > > >> >>>>> reference that it is faster are the articles in the web
>> and a
>> > > use
>> > > > >> case
>> > > > >> >>>>> in
>> > > > >> >>>>> our application.
>> > > > >> >>>>>
>> > > > >> >>>>> Cons:
>> > > > >> >>>>>
>> > > > >> >>>>> - AEB#renderHead() needs to check whether there is EDB up
>> in
>> > the
>> > > > >> >>>>>
>> > > > >> >>>> hierarchy
>> > > > >> >>>>
>> > > > >> >>>>> to be able to decide what to do.
>> > > > >> >>>>> This is ugly, I agree. But I see no other solution that
>> will
>> > > > preserve
>> > > > >> >>>>> the
>> > > > >> >>>>> transparent usage of something like EDB and will not
>> require a
>> > > > major
>> > > > >> >>>>> rewrite of user applications to be able to use event
>> > delegation.
>> > > > >> >>>>> -- there are some optimizations to lower the impact of the
>> new
>> > > > >> checks:
>> > > > >> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a
>> > > global
>> > > > >> >>>>> property
>> > > > >> >>>>> that prevents visiting the parent components and their
>> > behaviors
>> > > > for
>> > > > >> all
>> > > > >> >>>>> apps which do not use EDB
>> > > > >> >>>>> --- when EDB is bound it registers a metadata for its event
>> > type
>> > > > in
>> > > > >> the
>> > > > >> >>>>> page instance. This prevents visiting all behaviors of all
>> > > parent
>> > > > >> >>>>> components
>> > > > >> >>>>>
>> > > > >> >>>>>
>> > > > >> >>>>> I have no more ideas how to further optimize it.
>> > > > >> >>>>>
>> > > > >> >>>>> Any feedback is welcome! Even if you have a completely
>> > different
>> > > > idea
>> > > > >> >>>>> how
>> > > > >> >>>>> to implement this functionality.
>> > > > >> >>>>>
>> > > > >> >>>>> Thanks for reading!
>> > > > >> >>>>>
>> > > > >> >>>>>
>> > > > >> >>
>> > > > >>
>> > > >
>> > >
>> >
>>
>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Sebastien <se...@gmail.com>.
Hi Martin

> I think Igor said the opposite - bind on document with child selector:
> $(document).on('click', '[data-w-click]', handler);

I probably miss something somewhere in the explanation, then... Sorry for
the confusion!

> Behavior#bind(Component) gives you a reference to the component. No need
to
> pass it as ctor parameter.

Absolutely, and better because we can automatically know whether the
component is-a [web]page or another component...

> According to this article it is better to bind on the table than on the
> document.
> At least this is how I read it.

Yes, me too, that's what I tried to say... (Summer evening's agenda does
not helps to say something intelligent/understandable during the day... :s)

Anyway, I will try to look at your implementation asap and if possible
provide my own answer to your questions and try to help you on this topic
because it is an important feature for me (also)...

Best regards,
Sebastien.



On Wed, Jul 31, 2013 at 3:04 PM, Martin Grigorov <mg...@apache.org>wrote:

> Hi Sebastien,
>
>
> On Wed, Jul 31, 2013 at 2:37 PM, Sebastien <se...@gmail.com> wrote:
>
> > Hi,
> >
> > I agree with Igor. Just have to take care that binding to document is
> not a
> > default behavior for all bound events, and that it should be use with
> care.
> >
>
> I think Igor said the opposite - bind on document with child selector:
> $(document).on('click', '[data-w-click]', handler);
>
>
> > (IMHO, the javadoc should be enough)
> > Martin, I unfortunately did not had the chance to have a look at your
> code
> > now... :s But, if it not the case, if think EventDelegatingBehavior
> should
> > have a constructor that take a component (the datatable for instance) as
> an
> >
>
> Behavior#bind(Component) gives you a reference to the component. No need to
> pass it as ctor parameter.
>
>
> > argument so the event can be bound it it instead of the document (by
> > getting its markupid as the selector)...
>
>
> > [quote]
> > Here is another important tips "*Attaching many delegated event handlers
> > near the top of the document tree can degrade performance*". For best
> > performance, attach delegated events at a document location as close as
> > possible to the target elements as done in above jQuery code
> > [/quote]
> >
> >
> >
> http://jquerybyexample.blogspot.com/2013/04/direct-vs-delegated-events-with-jquery-on-method.html
>
>
> According to this article it is better to bind on the table than on the
> document.
> At least this is how I read it.
>
>
> >
> >
> >
> > I think events delegation would be a great addition anyhow...
> >
> > Best regards,
> > Sebastien.
> >
> >
> From the discussion so far I see these two issues which need to be solved
> before starting make changes in the code:
>
> 1) it must be possible to use event delegation on "the table", not only on
> the document
> 2) if the ajax attributes are stored in data-w-eventName attribute on the
> element then we need to figure out where to store the Ajax call listeners'
> functions
>
>
> For 1) I see it as a new Ajax request attribute - bindOnComponent = true
> If the attribute is 'false' then the event will be bound on the document
> instead and will use event delegation.
> This way all <td>s can return false, and the <table> can return true to
> handle the bubbling event (and stop it, because otherwise it will be
> handled twice when the event reaches the document)
>
> For 2) We can introduce global registry (e.g.
> Wicket.Ajax.Handlers[markupId][handlerType] =>
> Wicket.Ajax.Handlers["someId12"]["success"] = function(...) {...}).
> So the "global" handler (bound on the table or on the document) can use the
> specific Ajax call listeners for the clicked element with attribute
> 'data-w-eventName'
>
> WDYT ?
>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
Hi Sebastien,


On Wed, Jul 31, 2013 at 2:37 PM, Sebastien <se...@gmail.com> wrote:

> Hi,
>
> I agree with Igor. Just have to take care that binding to document is not a
> default behavior for all bound events, and that it should be use with care.
>

I think Igor said the opposite - bind on document with child selector:
$(document).on('click', '[data-w-click]', handler);


> (IMHO, the javadoc should be enough)
> Martin, I unfortunately did not had the chance to have a look at your code
> now... :s But, if it not the case, if think EventDelegatingBehavior should
> have a constructor that take a component (the datatable for instance) as an
>

Behavior#bind(Component) gives you a reference to the component. No need to
pass it as ctor parameter.


> argument so the event can be bound it it instead of the document (by
> getting its markupid as the selector)...


> [quote]
> Here is another important tips "*Attaching many delegated event handlers
> near the top of the document tree can degrade performance*". For best
> performance, attach delegated events at a document location as close as
> possible to the target elements as done in above jQuery code
> [/quote]
>
>
> http://jquerybyexample.blogspot.com/2013/04/direct-vs-delegated-events-with-jquery-on-method.html


According to this article it is better to bind on the table than on the
document.
At least this is how I read it.


>
>
>
> I think events delegation would be a great addition anyhow...
>
> Best regards,
> Sebastien.
>
>
>From the discussion so far I see these two issues which need to be solved
before starting make changes in the code:

1) it must be possible to use event delegation on "the table", not only on
the document
2) if the ajax attributes are stored in data-w-eventName attribute on the
element then we need to figure out where to store the Ajax call listeners'
functions


For 1) I see it as a new Ajax request attribute - bindOnComponent = true
If the attribute is 'false' then the event will be bound on the document
instead and will use event delegation.
This way all <td>s can return false, and the <table> can return true to
handle the bubbling event (and stop it, because otherwise it will be
handled twice when the event reaches the document)

For 2) We can introduce global registry (e.g.
Wicket.Ajax.Handlers[markupId][handlerType] =>
Wicket.Ajax.Handlers["someId12"]["success"] = function(...) {...}).
So the "global" handler (bound on the table or on the document) can use the
specific Ajax call listeners for the clicked element with attribute
'data-w-eventName'

WDYT ?



>
>
> On Tue, Jul 30, 2013 at 7:07 PM, Igor Vaynberg <igor.vaynberg@gmail.com
> >wrote:
>
> > binding on document is fine, you just have to make sure your code is fast
> > in case you are binding to things like mousemove.
> >
> > -igor
> >
> >
> > On Tue, Jul 30, 2013 at 1:31 AM, Martin Grigorov <mgrigorov@apache.org
> > >wrote:
> >
> > > On Fri, Jul 12, 2013 at 5:21 PM, Igor Vaynberg <
> igor.vaynberg@gmail.com
> > > >wrote:
> > >
> > > > On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <
> mgrigorov@apache.org
> > >
> > > > wrote:
> > > > > On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <
> > > igor.vaynberg@gmail.com
> > > > >wrote:
> > > > >
> > > > >> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <
> > > mgrigorov@apache.org>
> > > > >> wrote:
> > > > >> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net>
> > > wrote:
> > > > >> >
> > > > >> >> Hi,
> > > > >> >>
> > > > >> >>
> > > > >> >> >The idea with plain JS solution I cannot visualize in my head
> > yet.
> > > > >> >>
> > > > >> >> EventDelegatingBehavior is just a collector of JavaScript
> > snippets.
> > > > The
> > > > >> >> actual magic runs in the browser: a custom bubbling of events
> and
> > > > >> >> delegation to the actual behavior.
> > > > >> >> It should be possible to do this plain with JavaScript:
> > > > >> >>
> > > > >> >>   public class DelegatingAjax implements IAjax {
> > > > >> >>
> > > > >> >>     public ajax(IHeaderResponse response, Component component,
> > > > >> >> AjaxRequestAttributes attributes) {
> > > > >> >>       CharSequence ajaxAttributes =
> > > renderAjaxAttributes(**component,
> > > > >> >> attributes);
> > > > >> >>
> > > > >> >>
> > > > >>
> > > >
> > >
> >
> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
> > > > >> >> + ajaxAttributes + ");");
> > > > >> >>     }
> > > > >> >>   }
> > > > >> >>
> > > > >> >> This would be page-global though.
> > > > >> >
> > > > >> >
> > > > >> > This is an important detail!
> > > > >> > I'll consult with my frontend colleagues but so far I don't see
> > > > problems.
> > > > >> >
> > > > >> > For every delegated component we can set special CSS class, e.g.
> > > > >> > 'wicket-delegated'.
> > > > >> > The binding will be: $(document).on('click',
> '.wicket-delegated',
> > > > >> > function(event) {....})
> > > > >> > i.e. we will take advantage of jQuery delegation/live support.
> > > > >> > This way even newly added items in the repeaters will be
> > > automatically
> > > > >> > supported.
> > > > >>
> > > > >>
> > > > >> this is partially on the right track, but there are still some
> > > > >> optimization that can be made.
> > > > >>
> > > > >> first, the ajax attributes need to be moved into a data attribute
> > that
> > > > >> is written out on the tag. the final output of attaching a onclick
> > > > >> ajax behavior to a tag should end up looking like this:
> > > > >>
> > > > >> <a wicket:id="ajaxlink"
> > > > >> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
> > > > >>
> > > > >> (we will need to figure out how to encode ajax attributes into a
> > > string)
> > > > >>
> > > > >
> > > > > example:
> > > > > <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
> > > > >
> > > > > $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
> > > > >
> > > > > This works for valid JSON, but it doesn't for the enhancement we
> use
> > -
> > > > the
> > > > > functions for the call listeners.
> > > >
> > > > i did say we need to figure out a way to encode it right above the
> > > example
> > > > :)
> > > >
> > > > >> then you can have the one global listener:
> > > > >>
> > > > >> $(document).on("click", function(e) {
> > > > >>
> > > > >
> > > > > The problem here is that using 'document' will make the things
> > actually
> > > > > slower.
> > > > > We need to find a simple way to be able to bind on a parent
> > component.
> > > > > In Sven's example - a table with many cells the most appropriate
> > > element
> > > > is
> > > > > the <table> itself.
> > > >
> > > > umm, why does it make things slower exactly? this has virtually no
> > > > overhead, events bubble up anyways...so where does the slowness come
> > > > from?
> > > >
> > >
> > > All the talks about the deprecation of jQuery#live() say that binding
> on
> > > the document is not a good idea (performance wise).
> > >
> > > If it is not possible to bind on a context element then I see no much
> > > benefit.
> > >
> > >
> > > >
> > > > -igor
> > > >
> > > > >
> > > > > In event-delegating-behavior branch I need to traverse the parent
> > > > > components and their behaviors to be able to find the appropriate
> > > parent.
> > > > > So we win some performance in JS execution but lose some in Java
> :-/
> > > > >
> > > > >    var element=$(this), attrs=element.attr("data-w-click");
> > > > >>    if (attrs&&!e.handledByWicket)
> > > > >>        Wicket.Ajax.call(attrs);
> > > > >>        e.handledByWicket=true; // if there are more handlers
> above,
> > do
> > > > >> not double process the event - read below
> > > > >>    }
> > > > >> }
> > > > >>
> > > > >> the advantage here is that we only have one javascript listener
> that
> > > > >> needs to be registered.
> > > > >>
> > > > >> however, there are a few disadvantages:
> > > > >> * event propagation options wont work anymore, because the event
> has
> > > > >> to propagate all the way to the document in order to trigger.
> > > > >> * some libraries block events. for example if there is a panel
> with
> > an
> > > > >> ajax link inside a third party modal window. the modal window lib
> > may
> > > > >> prevent any clicks from propagating out of itself, which means the
> > > > >> handler on the document will never see them.
> > > > >>
> > > > >> we can sort of solve this by having a behavior that would write
> out
> > > > >> the listener above, but attached to the component not the
> document.
> > > > >>
> > > > >> that way, if we look at my example with the panel inside the
> modal,
> > > > >> the user can add this behavior to the panel that will be in the
> > modal
> > > > >> and still be able to capture the event.
> > > > >>
> > > > >> this does, however, make troubleshooting more difficult. why didnt
> > my
> > > > >> ajax event trigger? you will have to be a lot more aware about
> what
> > > > >> javascript you have in the dom.
> > > > >
> > > > >
> > > > >> i think a short term goal might be to move the ajax attributes
> into
> > a
> > > > >> dom attribute and change our ajax code to simply say
> > > > >> Wicket.Ajax.bind("click", "component234");
> > > > >>
> > > > >
> > > > > see above (valid JSON)
> > > > >
> > > > > we can enrich the DOM:
> > > > > <a ... onsuccess="someScript">
> > > > > but I think this is a step back to Wicket 1.5 days (ajax decorators
> > on
> > > > > strings, etc.)
> > > > >
> > > > >
> > > > >>
> > > > >> this will register the listener like above on the element
> directly.
> > so
> > > > >> no delegation yet but cleaner javascript/html. also the browser
> > doesnt
> > > > >> have to parse as much javascript, so it will be a bit speedier.
> > > > >>
> > > > >> potentially we can collect ids to further optimize js size:
> > > > >> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
> > > > >>
> > > > >> -igor
> > > > >>
> > > > >>
> > > > >> >
> > > > >> >
> > > > >> >>
> > > > >> >>
> > > > >> >> Sven
> > > > >> >>
> > > > >> >>
> > > > >> >>
> > > > >> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
> > > > >> >>
> > > > >> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <
> nbpratt@gmail.com>
> > > > wrote:
> > > > >> >>>
> > > > >> >>>  I think this is great - we have some tables now with a ton of
> > JS
> > > > >> events
> > > > >> >>>> on
> > > > >> >>>> the child elements.  Just to clarify, will this make the
> > rendered
> > > > page
> > > > >> >>>> smaller since there will only be a single JS handler for the
> > > event
> > > > for
> > > > >> >>>> the
> > > > >> >>>> container rather than N JS handlers?
> > > > >> >>>>
> > > > >> >>>>  At the moment all attributes for an inner element are
> > preserved.
> > > > >> >>> 'e' (the event name), 'c' (the component markup id), pd
> (prevent
> > > > >> default),
> > > > >> >>> sp (stop propagation) can be removed because they are not
> really
> > > > used.
> > > > >> >>> But every inner element can have its own call listeners, form
> > > > >> submitters
> > > > >> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I
> think
> > > > they
> > > > >> have
> > > > >> >>> to be preserved.
> > > > >> >>> If you look in #updateAjaxAttributes() for your ajax behaviors
> > in
> > > > your
> > > > >> >>> table cells you will probably notice that they have their own
> > > > >> attributes.
> > > > >> >>>
> > > > >> >>>
> > > > >> >>>  Making it switchable (I think how Sven suggested) would be an
> > > > >> >>>> improvement -
> > > > >> >>>> we could leave it off by default, but provide a simple switch
> > on
> > > a
> > > > >> >>>> per-container (or per-app) basis that would allow the dev to
> > > > choose.
> > > > >> >>>>
> > > > >> >>>>  Yes, it looks as an improvement.
> > > > >> >>> Moving the current code to such implementation is easy.
> > > > >> >>> The idea with plain JS solution I cannot visualize in my head
> > yet.
> > > > >> >>>
> > > > >> >>>
> > > > >> >>>  Regards
> > > > >> >>>>
> > > > >> >>>> Nick
> > > > >> >>>>
> > > > >> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
> > > > >> mgrigorov@apache.org
> > > > >> >>>>
> > > > >> >>>>> wrote:
> > > > >> >>>>> Hi,
> > > > >> >>>>>
> > > > >> >>>>> At https://github.com/apache/**wicket/compare/event-**
> > > > >> >>>>> delegating-behavioryou<
> > > > >>
> > https://github.com/apache/wicket/compare/event-delegating-behavioryou
> > > >
> > > > >> >>>>> may see the diff between master and
> event-delegating-behavior
> > > > >> branches.
> > > > >> >>>>>
> > > > >> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
> > > > >> >>>>>
> > > > >> >>>> EventDelegatingBehavior
> > > > >> >>>>
> > > > >> >>>>> (EDB), that suppresses the JS event binding for all
> > > > >> AjaxEventBehaviors
> > > > >> >>>>>
> > > > >> >>>> for
> > > > >> >>>>
> > > > >> >>>>> a given event type (click, submit, change, ...) in the
> > children
> > > > >> >>>>>
> > > > >> >>>> components
> > > > >> >>>>
> > > > >> >>>>> of the host component of EDB.
> > > > >> >>>>>
> > > > >> >>>>> How EDB works:
> > > > >> >>>>>
> > > > >> >>>>> - until now AjaxEventBehavior#renderHead() renders
> ondomready
> > > > header
> > > > >> >>>>> item
> > > > >> >>>>> with JS snippet like:
> > > > >> >>>>> Wicket.Ajax.ajax(**attributesObject);
> > > > >> >>>>> In the new branch there is a check if some parent has EDB
> for
> > > the
> > > > >> event
> > > > >> >>>>> type of this AEB, and if there is such then the AEB
> "donates"
> > > its
> > > > >> >>>>> attributes to the EDB.
> > > > >> >>>>>
> > > > >> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
> > > > >> >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
> > > > >> >>>>> edbAttributes,
> > > > >> >>>>> childrenAttrsMap);
> > > > >> >>>>>
> > > > >> >>>>> - when a delegated component fires its event (e.g. the user
> > > clicks
> > > > >> on an
> > > > >> >>>>> AjaxLink) the event is handled by EDB's event handler. It
> > > extracts
> > > > >> the
> > > > >> >>>>> markupId of the inner HTML element and fires
> Wicket.Ajax.Call
> > > with
> > > > >> the
> > > > >> >>>>> specific attributes for the extracted inner element.
> > > > >> >>>>>
> > > > >> >>>>> Pros:
> > > > >> >>>>>
> > > > >> >>>>> - simple to use - just add EDB to a container component
> around
> > > > your
> > > > >> Ajax
> > > > >> >>>>> heavy component (e.g. repeater with many Ajax behaviors).
> See
> > > the
> > > > >> demo
> > > > >> >>>>>
> > > > >> >>>> app
> > > > >> >>>>
> > > > >> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
> > > > >> https://issues.apache.org/jira/browse/WICKET-5267>
> > > > >> >>>>>
> > > > >> >>>>> -  faster JS execution
> > > > >> >>>>> -- faster execution of the domready handler because there is
> > > just
> > > > one
> > > > >> >>>>> binding instead of N
> > > > >> >>>>> -- faster reaction because the browser finds the event
> handler
> > > > much
> > > > >> >>>>>
> > > > >> >>>> faster.
> > > > >> >>>>
> > > > >> >>>>> I wasn't able to prove this with numbers because there is no
> > way
> > > > to
> > > > >> >>>>>
> > > > >> >>>> detect
> > > > >> >>>>
> > > > >> >>>>> the 'start time', i.e. when the user makes the action. With
> JS
> > > the
> > > > >> >>>>>
> > > > >> >>>> earliest
> > > > >> >>>>
> > > > >> >>>>> point is when the browser has already looked up the event
> > > handler.
> > > > >> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help
> > > too.
> > > > So
> > > > >> my
> > > > >> >>>>> reference that it is faster are the articles in the web and
> a
> > > use
> > > > >> case
> > > > >> >>>>> in
> > > > >> >>>>> our application.
> > > > >> >>>>>
> > > > >> >>>>> Cons:
> > > > >> >>>>>
> > > > >> >>>>> - AEB#renderHead() needs to check whether there is EDB up in
> > the
> > > > >> >>>>>
> > > > >> >>>> hierarchy
> > > > >> >>>>
> > > > >> >>>>> to be able to decide what to do.
> > > > >> >>>>> This is ugly, I agree. But I see no other solution that will
> > > > preserve
> > > > >> >>>>> the
> > > > >> >>>>> transparent usage of something like EDB and will not
> require a
> > > > major
> > > > >> >>>>> rewrite of user applications to be able to use event
> > delegation.
> > > > >> >>>>> -- there are some optimizations to lower the impact of the
> new
> > > > >> checks:
> > > > >> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a
> > > global
> > > > >> >>>>> property
> > > > >> >>>>> that prevents visiting the parent components and their
> > behaviors
> > > > for
> > > > >> all
> > > > >> >>>>> apps which do not use EDB
> > > > >> >>>>> --- when EDB is bound it registers a metadata for its event
> > type
> > > > in
> > > > >> the
> > > > >> >>>>> page instance. This prevents visiting all behaviors of all
> > > parent
> > > > >> >>>>> components
> > > > >> >>>>>
> > > > >> >>>>>
> > > > >> >>>>> I have no more ideas how to further optimize it.
> > > > >> >>>>>
> > > > >> >>>>> Any feedback is welcome! Even if you have a completely
> > different
> > > > idea
> > > > >> >>>>> how
> > > > >> >>>>> to implement this functionality.
> > > > >> >>>>>
> > > > >> >>>>> Thanks for reading!
> > > > >> >>>>>
> > > > >> >>>>>
> > > > >> >>
> > > > >>
> > > >
> > >
> >
>

Re: Ajax behavior for JavaScript event delegation

Posted by Sebastien <se...@gmail.com>.
Hi,

I agree with Igor. Just have to take care that binding to document is not a
default behavior for all bound events, and that it should be use with care.
(IMHO, the javadoc should be enough)
Martin, I unfortunately did not had the chance to have a look at your code
now... :s But, if it not the case, if think EventDelegatingBehavior should
have a constructor that take a component (the datatable for instance) as an
argument so the event can be bound it it instead of the document (by
getting its markupid as the selector)...

[quote]
Here is another important tips "*Attaching many delegated event handlers
near the top of the document tree can degrade performance*". For best
performance, attach delegated events at a document location as close as
possible to the target elements as done in above jQuery code
[/quote]

http://jquerybyexample.blogspot.com/2013/04/direct-vs-delegated-events-with-jquery-on-method.html


I think events delegation would be a great addition anyhow...

Best regards,
Sebastien.




On Tue, Jul 30, 2013 at 7:07 PM, Igor Vaynberg <ig...@gmail.com>wrote:

> binding on document is fine, you just have to make sure your code is fast
> in case you are binding to things like mousemove.
>
> -igor
>
>
> On Tue, Jul 30, 2013 at 1:31 AM, Martin Grigorov <mgrigorov@apache.org
> >wrote:
>
> > On Fri, Jul 12, 2013 at 5:21 PM, Igor Vaynberg <igor.vaynberg@gmail.com
> > >wrote:
> >
> > > On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <mgrigorov@apache.org
> >
> > > wrote:
> > > > On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <
> > igor.vaynberg@gmail.com
> > > >wrote:
> > > >
> > > >> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <
> > mgrigorov@apache.org>
> > > >> wrote:
> > > >> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net>
> > wrote:
> > > >> >
> > > >> >> Hi,
> > > >> >>
> > > >> >>
> > > >> >> >The idea with plain JS solution I cannot visualize in my head
> yet.
> > > >> >>
> > > >> >> EventDelegatingBehavior is just a collector of JavaScript
> snippets.
> > > The
> > > >> >> actual magic runs in the browser: a custom bubbling of events and
> > > >> >> delegation to the actual behavior.
> > > >> >> It should be possible to do this plain with JavaScript:
> > > >> >>
> > > >> >>   public class DelegatingAjax implements IAjax {
> > > >> >>
> > > >> >>     public ajax(IHeaderResponse response, Component component,
> > > >> >> AjaxRequestAttributes attributes) {
> > > >> >>       CharSequence ajaxAttributes =
> > renderAjaxAttributes(**component,
> > > >> >> attributes);
> > > >> >>
> > > >> >>
> > > >>
> > >
> >
> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
> > > >> >> + ajaxAttributes + ");");
> > > >> >>     }
> > > >> >>   }
> > > >> >>
> > > >> >> This would be page-global though.
> > > >> >
> > > >> >
> > > >> > This is an important detail!
> > > >> > I'll consult with my frontend colleagues but so far I don't see
> > > problems.
> > > >> >
> > > >> > For every delegated component we can set special CSS class, e.g.
> > > >> > 'wicket-delegated'.
> > > >> > The binding will be: $(document).on('click', '.wicket-delegated',
> > > >> > function(event) {....})
> > > >> > i.e. we will take advantage of jQuery delegation/live support.
> > > >> > This way even newly added items in the repeaters will be
> > automatically
> > > >> > supported.
> > > >>
> > > >>
> > > >> this is partially on the right track, but there are still some
> > > >> optimization that can be made.
> > > >>
> > > >> first, the ajax attributes need to be moved into a data attribute
> that
> > > >> is written out on the tag. the final output of attaching a onclick
> > > >> ajax behavior to a tag should end up looking like this:
> > > >>
> > > >> <a wicket:id="ajaxlink"
> > > >> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
> > > >>
> > > >> (we will need to figure out how to encode ajax attributes into a
> > string)
> > > >>
> > > >
> > > > example:
> > > > <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
> > > >
> > > > $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
> > > >
> > > > This works for valid JSON, but it doesn't for the enhancement we use
> -
> > > the
> > > > functions for the call listeners.
> > >
> > > i did say we need to figure out a way to encode it right above the
> > example
> > > :)
> > >
> > > >> then you can have the one global listener:
> > > >>
> > > >> $(document).on("click", function(e) {
> > > >>
> > > >
> > > > The problem here is that using 'document' will make the things
> actually
> > > > slower.
> > > > We need to find a simple way to be able to bind on a parent
> component.
> > > > In Sven's example - a table with many cells the most appropriate
> > element
> > > is
> > > > the <table> itself.
> > >
> > > umm, why does it make things slower exactly? this has virtually no
> > > overhead, events bubble up anyways...so where does the slowness come
> > > from?
> > >
> >
> > All the talks about the deprecation of jQuery#live() say that binding on
> > the document is not a good idea (performance wise).
> >
> > If it is not possible to bind on a context element then I see no much
> > benefit.
> >
> >
> > >
> > > -igor
> > >
> > > >
> > > > In event-delegating-behavior branch I need to traverse the parent
> > > > components and their behaviors to be able to find the appropriate
> > parent.
> > > > So we win some performance in JS execution but lose some in Java :-/
> > > >
> > > >    var element=$(this), attrs=element.attr("data-w-click");
> > > >>    if (attrs&&!e.handledByWicket)
> > > >>        Wicket.Ajax.call(attrs);
> > > >>        e.handledByWicket=true; // if there are more handlers above,
> do
> > > >> not double process the event - read below
> > > >>    }
> > > >> }
> > > >>
> > > >> the advantage here is that we only have one javascript listener that
> > > >> needs to be registered.
> > > >>
> > > >> however, there are a few disadvantages:
> > > >> * event propagation options wont work anymore, because the event has
> > > >> to propagate all the way to the document in order to trigger.
> > > >> * some libraries block events. for example if there is a panel with
> an
> > > >> ajax link inside a third party modal window. the modal window lib
> may
> > > >> prevent any clicks from propagating out of itself, which means the
> > > >> handler on the document will never see them.
> > > >>
> > > >> we can sort of solve this by having a behavior that would write out
> > > >> the listener above, but attached to the component not the document.
> > > >>
> > > >> that way, if we look at my example with the panel inside the modal,
> > > >> the user can add this behavior to the panel that will be in the
> modal
> > > >> and still be able to capture the event.
> > > >>
> > > >> this does, however, make troubleshooting more difficult. why didnt
> my
> > > >> ajax event trigger? you will have to be a lot more aware about what
> > > >> javascript you have in the dom.
> > > >
> > > >
> > > >> i think a short term goal might be to move the ajax attributes into
> a
> > > >> dom attribute and change our ajax code to simply say
> > > >> Wicket.Ajax.bind("click", "component234");
> > > >>
> > > >
> > > > see above (valid JSON)
> > > >
> > > > we can enrich the DOM:
> > > > <a ... onsuccess="someScript">
> > > > but I think this is a step back to Wicket 1.5 days (ajax decorators
> on
> > > > strings, etc.)
> > > >
> > > >
> > > >>
> > > >> this will register the listener like above on the element directly.
> so
> > > >> no delegation yet but cleaner javascript/html. also the browser
> doesnt
> > > >> have to parse as much javascript, so it will be a bit speedier.
> > > >>
> > > >> potentially we can collect ids to further optimize js size:
> > > >> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
> > > >>
> > > >> -igor
> > > >>
> > > >>
> > > >> >
> > > >> >
> > > >> >>
> > > >> >>
> > > >> >> Sven
> > > >> >>
> > > >> >>
> > > >> >>
> > > >> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
> > > >> >>
> > > >> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com>
> > > wrote:
> > > >> >>>
> > > >> >>>  I think this is great - we have some tables now with a ton of
> JS
> > > >> events
> > > >> >>>> on
> > > >> >>>> the child elements.  Just to clarify, will this make the
> rendered
> > > page
> > > >> >>>> smaller since there will only be a single JS handler for the
> > event
> > > for
> > > >> >>>> the
> > > >> >>>> container rather than N JS handlers?
> > > >> >>>>
> > > >> >>>>  At the moment all attributes for an inner element are
> preserved.
> > > >> >>> 'e' (the event name), 'c' (the component markup id), pd (prevent
> > > >> default),
> > > >> >>> sp (stop propagation) can be removed because they are not really
> > > used.
> > > >> >>> But every inner element can have its own call listeners, form
> > > >> submitters
> > > >> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think
> > > they
> > > >> have
> > > >> >>> to be preserved.
> > > >> >>> If you look in #updateAjaxAttributes() for your ajax behaviors
> in
> > > your
> > > >> >>> table cells you will probably notice that they have their own
> > > >> attributes.
> > > >> >>>
> > > >> >>>
> > > >> >>>  Making it switchable (I think how Sven suggested) would be an
> > > >> >>>> improvement -
> > > >> >>>> we could leave it off by default, but provide a simple switch
> on
> > a
> > > >> >>>> per-container (or per-app) basis that would allow the dev to
> > > choose.
> > > >> >>>>
> > > >> >>>>  Yes, it looks as an improvement.
> > > >> >>> Moving the current code to such implementation is easy.
> > > >> >>> The idea with plain JS solution I cannot visualize in my head
> yet.
> > > >> >>>
> > > >> >>>
> > > >> >>>  Regards
> > > >> >>>>
> > > >> >>>> Nick
> > > >> >>>>
> > > >> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
> > > >> mgrigorov@apache.org
> > > >> >>>>
> > > >> >>>>> wrote:
> > > >> >>>>> Hi,
> > > >> >>>>>
> > > >> >>>>> At https://github.com/apache/**wicket/compare/event-**
> > > >> >>>>> delegating-behavioryou<
> > > >>
> https://github.com/apache/wicket/compare/event-delegating-behavioryou
> > >
> > > >> >>>>> may see the diff between master and event-delegating-behavior
> > > >> branches.
> > > >> >>>>>
> > > >> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
> > > >> >>>>>
> > > >> >>>> EventDelegatingBehavior
> > > >> >>>>
> > > >> >>>>> (EDB), that suppresses the JS event binding for all
> > > >> AjaxEventBehaviors
> > > >> >>>>>
> > > >> >>>> for
> > > >> >>>>
> > > >> >>>>> a given event type (click, submit, change, ...) in the
> children
> > > >> >>>>>
> > > >> >>>> components
> > > >> >>>>
> > > >> >>>>> of the host component of EDB.
> > > >> >>>>>
> > > >> >>>>> How EDB works:
> > > >> >>>>>
> > > >> >>>>> - until now AjaxEventBehavior#renderHead() renders ondomready
> > > header
> > > >> >>>>> item
> > > >> >>>>> with JS snippet like:
> > > >> >>>>> Wicket.Ajax.ajax(**attributesObject);
> > > >> >>>>> In the new branch there is a check if some parent has EDB for
> > the
> > > >> event
> > > >> >>>>> type of this AEB, and if there is such then the AEB "donates"
> > its
> > > >> >>>>> attributes to the EDB.
> > > >> >>>>>
> > > >> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
> > > >> >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
> > > >> >>>>> edbAttributes,
> > > >> >>>>> childrenAttrsMap);
> > > >> >>>>>
> > > >> >>>>> - when a delegated component fires its event (e.g. the user
> > clicks
> > > >> on an
> > > >> >>>>> AjaxLink) the event is handled by EDB's event handler. It
> > extracts
> > > >> the
> > > >> >>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call
> > with
> > > >> the
> > > >> >>>>> specific attributes for the extracted inner element.
> > > >> >>>>>
> > > >> >>>>> Pros:
> > > >> >>>>>
> > > >> >>>>> - simple to use - just add EDB to a container component around
> > > your
> > > >> Ajax
> > > >> >>>>> heavy component (e.g. repeater with many Ajax behaviors). See
> > the
> > > >> demo
> > > >> >>>>>
> > > >> >>>> app
> > > >> >>>>
> > > >> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
> > > >> https://issues.apache.org/jira/browse/WICKET-5267>
> > > >> >>>>>
> > > >> >>>>> -  faster JS execution
> > > >> >>>>> -- faster execution of the domready handler because there is
> > just
> > > one
> > > >> >>>>> binding instead of N
> > > >> >>>>> -- faster reaction because the browser finds the event handler
> > > much
> > > >> >>>>>
> > > >> >>>> faster.
> > > >> >>>>
> > > >> >>>>> I wasn't able to prove this with numbers because there is no
> way
> > > to
> > > >> >>>>>
> > > >> >>>> detect
> > > >> >>>>
> > > >> >>>>> the 'start time', i.e. when the user makes the action. With JS
> > the
> > > >> >>>>>
> > > >> >>>> earliest
> > > >> >>>>
> > > >> >>>>> point is when the browser has already looked up the event
> > handler.
> > > >> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help
> > too.
> > > So
> > > >> my
> > > >> >>>>> reference that it is faster are the articles in the web and a
> > use
> > > >> case
> > > >> >>>>> in
> > > >> >>>>> our application.
> > > >> >>>>>
> > > >> >>>>> Cons:
> > > >> >>>>>
> > > >> >>>>> - AEB#renderHead() needs to check whether there is EDB up in
> the
> > > >> >>>>>
> > > >> >>>> hierarchy
> > > >> >>>>
> > > >> >>>>> to be able to decide what to do.
> > > >> >>>>> This is ugly, I agree. But I see no other solution that will
> > > preserve
> > > >> >>>>> the
> > > >> >>>>> transparent usage of something like EDB and will not require a
> > > major
> > > >> >>>>> rewrite of user applications to be able to use event
> delegation.
> > > >> >>>>> -- there are some optimizations to lower the impact of the new
> > > >> checks:
> > > >> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a
> > global
> > > >> >>>>> property
> > > >> >>>>> that prevents visiting the parent components and their
> behaviors
> > > for
> > > >> all
> > > >> >>>>> apps which do not use EDB
> > > >> >>>>> --- when EDB is bound it registers a metadata for its event
> type
> > > in
> > > >> the
> > > >> >>>>> page instance. This prevents visiting all behaviors of all
> > parent
> > > >> >>>>> components
> > > >> >>>>>
> > > >> >>>>>
> > > >> >>>>> I have no more ideas how to further optimize it.
> > > >> >>>>>
> > > >> >>>>> Any feedback is welcome! Even if you have a completely
> different
> > > idea
> > > >> >>>>> how
> > > >> >>>>> to implement this functionality.
> > > >> >>>>>
> > > >> >>>>> Thanks for reading!
> > > >> >>>>>
> > > >> >>>>>
> > > >> >>
> > > >>
> > >
> >
>

Re: Ajax behavior for JavaScript event delegation

Posted by Igor Vaynberg <ig...@gmail.com>.
binding on document is fine, you just have to make sure your code is fast
in case you are binding to things like mousemove.

-igor


On Tue, Jul 30, 2013 at 1:31 AM, Martin Grigorov <mg...@apache.org>wrote:

> On Fri, Jul 12, 2013 at 5:21 PM, Igor Vaynberg <igor.vaynberg@gmail.com
> >wrote:
>
> > On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <mg...@apache.org>
> > wrote:
> > > On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <
> igor.vaynberg@gmail.com
> > >wrote:
> > >
> > >> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <
> mgrigorov@apache.org>
> > >> wrote:
> > >> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net>
> wrote:
> > >> >
> > >> >> Hi,
> > >> >>
> > >> >>
> > >> >> >The idea with plain JS solution I cannot visualize in my head yet.
> > >> >>
> > >> >> EventDelegatingBehavior is just a collector of JavaScript snippets.
> > The
> > >> >> actual magic runs in the browser: a custom bubbling of events and
> > >> >> delegation to the actual behavior.
> > >> >> It should be possible to do this plain with JavaScript:
> > >> >>
> > >> >>   public class DelegatingAjax implements IAjax {
> > >> >>
> > >> >>     public ajax(IHeaderResponse response, Component component,
> > >> >> AjaxRequestAttributes attributes) {
> > >> >>       CharSequence ajaxAttributes =
> renderAjaxAttributes(**component,
> > >> >> attributes);
> > >> >>
> > >> >>
> > >>
> >
> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
> > >> >> + ajaxAttributes + ");");
> > >> >>     }
> > >> >>   }
> > >> >>
> > >> >> This would be page-global though.
> > >> >
> > >> >
> > >> > This is an important detail!
> > >> > I'll consult with my frontend colleagues but so far I don't see
> > problems.
> > >> >
> > >> > For every delegated component we can set special CSS class, e.g.
> > >> > 'wicket-delegated'.
> > >> > The binding will be: $(document).on('click', '.wicket-delegated',
> > >> > function(event) {....})
> > >> > i.e. we will take advantage of jQuery delegation/live support.
> > >> > This way even newly added items in the repeaters will be
> automatically
> > >> > supported.
> > >>
> > >>
> > >> this is partially on the right track, but there are still some
> > >> optimization that can be made.
> > >>
> > >> first, the ajax attributes need to be moved into a data attribute that
> > >> is written out on the tag. the final output of attaching a onclick
> > >> ajax behavior to a tag should end up looking like this:
> > >>
> > >> <a wicket:id="ajaxlink"
> > >> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
> > >>
> > >> (we will need to figure out how to encode ajax attributes into a
> string)
> > >>
> > >
> > > example:
> > > <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
> > >
> > > $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
> > >
> > > This works for valid JSON, but it doesn't for the enhancement we use -
> > the
> > > functions for the call listeners.
> >
> > i did say we need to figure out a way to encode it right above the
> example
> > :)
> >
> > >> then you can have the one global listener:
> > >>
> > >> $(document).on("click", function(e) {
> > >>
> > >
> > > The problem here is that using 'document' will make the things actually
> > > slower.
> > > We need to find a simple way to be able to bind on a parent component.
> > > In Sven's example - a table with many cells the most appropriate
> element
> > is
> > > the <table> itself.
> >
> > umm, why does it make things slower exactly? this has virtually no
> > overhead, events bubble up anyways...so where does the slowness come
> > from?
> >
>
> All the talks about the deprecation of jQuery#live() say that binding on
> the document is not a good idea (performance wise).
>
> If it is not possible to bind on a context element then I see no much
> benefit.
>
>
> >
> > -igor
> >
> > >
> > > In event-delegating-behavior branch I need to traverse the parent
> > > components and their behaviors to be able to find the appropriate
> parent.
> > > So we win some performance in JS execution but lose some in Java :-/
> > >
> > >    var element=$(this), attrs=element.attr("data-w-click");
> > >>    if (attrs&&!e.handledByWicket)
> > >>        Wicket.Ajax.call(attrs);
> > >>        e.handledByWicket=true; // if there are more handlers above, do
> > >> not double process the event - read below
> > >>    }
> > >> }
> > >>
> > >> the advantage here is that we only have one javascript listener that
> > >> needs to be registered.
> > >>
> > >> however, there are a few disadvantages:
> > >> * event propagation options wont work anymore, because the event has
> > >> to propagate all the way to the document in order to trigger.
> > >> * some libraries block events. for example if there is a panel with an
> > >> ajax link inside a third party modal window. the modal window lib may
> > >> prevent any clicks from propagating out of itself, which means the
> > >> handler on the document will never see them.
> > >>
> > >> we can sort of solve this by having a behavior that would write out
> > >> the listener above, but attached to the component not the document.
> > >>
> > >> that way, if we look at my example with the panel inside the modal,
> > >> the user can add this behavior to the panel that will be in the modal
> > >> and still be able to capture the event.
> > >>
> > >> this does, however, make troubleshooting more difficult. why didnt my
> > >> ajax event trigger? you will have to be a lot more aware about what
> > >> javascript you have in the dom.
> > >
> > >
> > >> i think a short term goal might be to move the ajax attributes into a
> > >> dom attribute and change our ajax code to simply say
> > >> Wicket.Ajax.bind("click", "component234");
> > >>
> > >
> > > see above (valid JSON)
> > >
> > > we can enrich the DOM:
> > > <a ... onsuccess="someScript">
> > > but I think this is a step back to Wicket 1.5 days (ajax decorators on
> > > strings, etc.)
> > >
> > >
> > >>
> > >> this will register the listener like above on the element directly. so
> > >> no delegation yet but cleaner javascript/html. also the browser doesnt
> > >> have to parse as much javascript, so it will be a bit speedier.
> > >>
> > >> potentially we can collect ids to further optimize js size:
> > >> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
> > >>
> > >> -igor
> > >>
> > >>
> > >> >
> > >> >
> > >> >>
> > >> >>
> > >> >> Sven
> > >> >>
> > >> >>
> > >> >>
> > >> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
> > >> >>
> > >> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com>
> > wrote:
> > >> >>>
> > >> >>>  I think this is great - we have some tables now with a ton of JS
> > >> events
> > >> >>>> on
> > >> >>>> the child elements.  Just to clarify, will this make the rendered
> > page
> > >> >>>> smaller since there will only be a single JS handler for the
> event
> > for
> > >> >>>> the
> > >> >>>> container rather than N JS handlers?
> > >> >>>>
> > >> >>>>  At the moment all attributes for an inner element are preserved.
> > >> >>> 'e' (the event name), 'c' (the component markup id), pd (prevent
> > >> default),
> > >> >>> sp (stop propagation) can be removed because they are not really
> > used.
> > >> >>> But every inner element can have its own call listeners, form
> > >> submitters
> > >> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think
> > they
> > >> have
> > >> >>> to be preserved.
> > >> >>> If you look in #updateAjaxAttributes() for your ajax behaviors in
> > your
> > >> >>> table cells you will probably notice that they have their own
> > >> attributes.
> > >> >>>
> > >> >>>
> > >> >>>  Making it switchable (I think how Sven suggested) would be an
> > >> >>>> improvement -
> > >> >>>> we could leave it off by default, but provide a simple switch on
> a
> > >> >>>> per-container (or per-app) basis that would allow the dev to
> > choose.
> > >> >>>>
> > >> >>>>  Yes, it looks as an improvement.
> > >> >>> Moving the current code to such implementation is easy.
> > >> >>> The idea with plain JS solution I cannot visualize in my head yet.
> > >> >>>
> > >> >>>
> > >> >>>  Regards
> > >> >>>>
> > >> >>>> Nick
> > >> >>>>
> > >> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
> > >> mgrigorov@apache.org
> > >> >>>>
> > >> >>>>> wrote:
> > >> >>>>> Hi,
> > >> >>>>>
> > >> >>>>> At https://github.com/apache/**wicket/compare/event-**
> > >> >>>>> delegating-behavioryou<
> > >> https://github.com/apache/wicket/compare/event-delegating-behavioryou
> >
> > >> >>>>> may see the diff between master and event-delegating-behavior
> > >> branches.
> > >> >>>>>
> > >> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
> > >> >>>>>
> > >> >>>> EventDelegatingBehavior
> > >> >>>>
> > >> >>>>> (EDB), that suppresses the JS event binding for all
> > >> AjaxEventBehaviors
> > >> >>>>>
> > >> >>>> for
> > >> >>>>
> > >> >>>>> a given event type (click, submit, change, ...) in the children
> > >> >>>>>
> > >> >>>> components
> > >> >>>>
> > >> >>>>> of the host component of EDB.
> > >> >>>>>
> > >> >>>>> How EDB works:
> > >> >>>>>
> > >> >>>>> - until now AjaxEventBehavior#renderHead() renders ondomready
> > header
> > >> >>>>> item
> > >> >>>>> with JS snippet like:
> > >> >>>>> Wicket.Ajax.ajax(**attributesObject);
> > >> >>>>> In the new branch there is a check if some parent has EDB for
> the
> > >> event
> > >> >>>>> type of this AEB, and if there is such then the AEB "donates"
> its
> > >> >>>>> attributes to the EDB.
> > >> >>>>>
> > >> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
> > >> >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
> > >> >>>>> edbAttributes,
> > >> >>>>> childrenAttrsMap);
> > >> >>>>>
> > >> >>>>> - when a delegated component fires its event (e.g. the user
> clicks
> > >> on an
> > >> >>>>> AjaxLink) the event is handled by EDB's event handler. It
> extracts
> > >> the
> > >> >>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call
> with
> > >> the
> > >> >>>>> specific attributes for the extracted inner element.
> > >> >>>>>
> > >> >>>>> Pros:
> > >> >>>>>
> > >> >>>>> - simple to use - just add EDB to a container component around
> > your
> > >> Ajax
> > >> >>>>> heavy component (e.g. repeater with many Ajax behaviors). See
> the
> > >> demo
> > >> >>>>>
> > >> >>>> app
> > >> >>>>
> > >> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
> > >> https://issues.apache.org/jira/browse/WICKET-5267>
> > >> >>>>>
> > >> >>>>> -  faster JS execution
> > >> >>>>> -- faster execution of the domready handler because there is
> just
> > one
> > >> >>>>> binding instead of N
> > >> >>>>> -- faster reaction because the browser finds the event handler
> > much
> > >> >>>>>
> > >> >>>> faster.
> > >> >>>>
> > >> >>>>> I wasn't able to prove this with numbers because there is no way
> > to
> > >> >>>>>
> > >> >>>> detect
> > >> >>>>
> > >> >>>>> the 'start time', i.e. when the user makes the action. With JS
> the
> > >> >>>>>
> > >> >>>> earliest
> > >> >>>>
> > >> >>>>> point is when the browser has already looked up the event
> handler.
> > >> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help
> too.
> > So
> > >> my
> > >> >>>>> reference that it is faster are the articles in the web and a
> use
> > >> case
> > >> >>>>> in
> > >> >>>>> our application.
> > >> >>>>>
> > >> >>>>> Cons:
> > >> >>>>>
> > >> >>>>> - AEB#renderHead() needs to check whether there is EDB up in the
> > >> >>>>>
> > >> >>>> hierarchy
> > >> >>>>
> > >> >>>>> to be able to decide what to do.
> > >> >>>>> This is ugly, I agree. But I see no other solution that will
> > preserve
> > >> >>>>> the
> > >> >>>>> transparent usage of something like EDB and will not require a
> > major
> > >> >>>>> rewrite of user applications to be able to use event delegation.
> > >> >>>>> -- there are some optimizations to lower the impact of the new
> > >> checks:
> > >> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a
> global
> > >> >>>>> property
> > >> >>>>> that prevents visiting the parent components and their behaviors
> > for
> > >> all
> > >> >>>>> apps which do not use EDB
> > >> >>>>> --- when EDB is bound it registers a metadata for its event type
> > in
> > >> the
> > >> >>>>> page instance. This prevents visiting all behaviors of all
> parent
> > >> >>>>> components
> > >> >>>>>
> > >> >>>>>
> > >> >>>>> I have no more ideas how to further optimize it.
> > >> >>>>>
> > >> >>>>> Any feedback is welcome! Even if you have a completely different
> > idea
> > >> >>>>> how
> > >> >>>>> to implement this functionality.
> > >> >>>>>
> > >> >>>>> Thanks for reading!
> > >> >>>>>
> > >> >>>>>
> > >> >>
> > >>
> >
>

Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
On Fri, Jul 12, 2013 at 5:21 PM, Igor Vaynberg <ig...@gmail.com>wrote:

> On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <mg...@apache.org>
> wrote:
> > On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <igor.vaynberg@gmail.com
> >wrote:
> >
> >> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <mg...@apache.org>
> >> wrote:
> >> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
> >> >
> >> >> Hi,
> >> >>
> >> >>
> >> >> >The idea with plain JS solution I cannot visualize in my head yet.
> >> >>
> >> >> EventDelegatingBehavior is just a collector of JavaScript snippets.
> The
> >> >> actual magic runs in the browser: a custom bubbling of events and
> >> >> delegation to the actual behavior.
> >> >> It should be possible to do this plain with JavaScript:
> >> >>
> >> >>   public class DelegatingAjax implements IAjax {
> >> >>
> >> >>     public ajax(IHeaderResponse response, Component component,
> >> >> AjaxRequestAttributes attributes) {
> >> >>       CharSequence ajaxAttributes = renderAjaxAttributes(**component,
> >> >> attributes);
> >> >>
> >> >>
> >>
> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
> >> >> + ajaxAttributes + ");");
> >> >>     }
> >> >>   }
> >> >>
> >> >> This would be page-global though.
> >> >
> >> >
> >> > This is an important detail!
> >> > I'll consult with my frontend colleagues but so far I don't see
> problems.
> >> >
> >> > For every delegated component we can set special CSS class, e.g.
> >> > 'wicket-delegated'.
> >> > The binding will be: $(document).on('click', '.wicket-delegated',
> >> > function(event) {....})
> >> > i.e. we will take advantage of jQuery delegation/live support.
> >> > This way even newly added items in the repeaters will be automatically
> >> > supported.
> >>
> >>
> >> this is partially on the right track, but there are still some
> >> optimization that can be made.
> >>
> >> first, the ajax attributes need to be moved into a data attribute that
> >> is written out on the tag. the final output of attaching a onclick
> >> ajax behavior to a tag should end up looking like this:
> >>
> >> <a wicket:id="ajaxlink"
> >> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
> >>
> >> (we will need to figure out how to encode ajax attributes into a string)
> >>
> >
> > example:
> > <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
> >
> > $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
> >
> > This works for valid JSON, but it doesn't for the enhancement we use -
> the
> > functions for the call listeners.
>
> i did say we need to figure out a way to encode it right above the example
> :)
>
> >> then you can have the one global listener:
> >>
> >> $(document).on("click", function(e) {
> >>
> >
> > The problem here is that using 'document' will make the things actually
> > slower.
> > We need to find a simple way to be able to bind on a parent component.
> > In Sven's example - a table with many cells the most appropriate element
> is
> > the <table> itself.
>
> umm, why does it make things slower exactly? this has virtually no
> overhead, events bubble up anyways...so where does the slowness come
> from?
>

All the talks about the deprecation of jQuery#live() say that binding on
the document is not a good idea (performance wise).

If it is not possible to bind on a context element then I see no much
benefit.


>
> -igor
>
> >
> > In event-delegating-behavior branch I need to traverse the parent
> > components and their behaviors to be able to find the appropriate parent.
> > So we win some performance in JS execution but lose some in Java :-/
> >
> >    var element=$(this), attrs=element.attr("data-w-click");
> >>    if (attrs&&!e.handledByWicket)
> >>        Wicket.Ajax.call(attrs);
> >>        e.handledByWicket=true; // if there are more handlers above, do
> >> not double process the event - read below
> >>    }
> >> }
> >>
> >> the advantage here is that we only have one javascript listener that
> >> needs to be registered.
> >>
> >> however, there are a few disadvantages:
> >> * event propagation options wont work anymore, because the event has
> >> to propagate all the way to the document in order to trigger.
> >> * some libraries block events. for example if there is a panel with an
> >> ajax link inside a third party modal window. the modal window lib may
> >> prevent any clicks from propagating out of itself, which means the
> >> handler on the document will never see them.
> >>
> >> we can sort of solve this by having a behavior that would write out
> >> the listener above, but attached to the component not the document.
> >>
> >> that way, if we look at my example with the panel inside the modal,
> >> the user can add this behavior to the panel that will be in the modal
> >> and still be able to capture the event.
> >>
> >> this does, however, make troubleshooting more difficult. why didnt my
> >> ajax event trigger? you will have to be a lot more aware about what
> >> javascript you have in the dom.
> >
> >
> >> i think a short term goal might be to move the ajax attributes into a
> >> dom attribute and change our ajax code to simply say
> >> Wicket.Ajax.bind("click", "component234");
> >>
> >
> > see above (valid JSON)
> >
> > we can enrich the DOM:
> > <a ... onsuccess="someScript">
> > but I think this is a step back to Wicket 1.5 days (ajax decorators on
> > strings, etc.)
> >
> >
> >>
> >> this will register the listener like above on the element directly. so
> >> no delegation yet but cleaner javascript/html. also the browser doesnt
> >> have to parse as much javascript, so it will be a bit speedier.
> >>
> >> potentially we can collect ids to further optimize js size:
> >> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
> >>
> >> -igor
> >>
> >>
> >> >
> >> >
> >> >>
> >> >>
> >> >> Sven
> >> >>
> >> >>
> >> >>
> >> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
> >> >>
> >> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com>
> wrote:
> >> >>>
> >> >>>  I think this is great - we have some tables now with a ton of JS
> >> events
> >> >>>> on
> >> >>>> the child elements.  Just to clarify, will this make the rendered
> page
> >> >>>> smaller since there will only be a single JS handler for the event
> for
> >> >>>> the
> >> >>>> container rather than N JS handlers?
> >> >>>>
> >> >>>>  At the moment all attributes for an inner element are preserved.
> >> >>> 'e' (the event name), 'c' (the component markup id), pd (prevent
> >> default),
> >> >>> sp (stop propagation) can be removed because they are not really
> used.
> >> >>> But every inner element can have its own call listeners, form
> >> submitters
> >> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think
> they
> >> have
> >> >>> to be preserved.
> >> >>> If you look in #updateAjaxAttributes() for your ajax behaviors in
> your
> >> >>> table cells you will probably notice that they have their own
> >> attributes.
> >> >>>
> >> >>>
> >> >>>  Making it switchable (I think how Sven suggested) would be an
> >> >>>> improvement -
> >> >>>> we could leave it off by default, but provide a simple switch on a
> >> >>>> per-container (or per-app) basis that would allow the dev to
> choose.
> >> >>>>
> >> >>>>  Yes, it looks as an improvement.
> >> >>> Moving the current code to such implementation is easy.
> >> >>> The idea with plain JS solution I cannot visualize in my head yet.
> >> >>>
> >> >>>
> >> >>>  Regards
> >> >>>>
> >> >>>> Nick
> >> >>>>
> >> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
> >> mgrigorov@apache.org
> >> >>>>
> >> >>>>> wrote:
> >> >>>>> Hi,
> >> >>>>>
> >> >>>>> At https://github.com/apache/**wicket/compare/event-**
> >> >>>>> delegating-behavioryou<
> >> https://github.com/apache/wicket/compare/event-delegating-behavioryou>
> >> >>>>> may see the diff between master and event-delegating-behavior
> >> branches.
> >> >>>>>
> >> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
> >> >>>>>
> >> >>>> EventDelegatingBehavior
> >> >>>>
> >> >>>>> (EDB), that suppresses the JS event binding for all
> >> AjaxEventBehaviors
> >> >>>>>
> >> >>>> for
> >> >>>>
> >> >>>>> a given event type (click, submit, change, ...) in the children
> >> >>>>>
> >> >>>> components
> >> >>>>
> >> >>>>> of the host component of EDB.
> >> >>>>>
> >> >>>>> How EDB works:
> >> >>>>>
> >> >>>>> - until now AjaxEventBehavior#renderHead() renders ondomready
> header
> >> >>>>> item
> >> >>>>> with JS snippet like:
> >> >>>>> Wicket.Ajax.ajax(**attributesObject);
> >> >>>>> In the new branch there is a check if some parent has EDB for the
> >> event
> >> >>>>> type of this AEB, and if there is such then the AEB "donates" its
> >> >>>>> attributes to the EDB.
> >> >>>>>
> >> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
> >> >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
> >> >>>>> edbAttributes,
> >> >>>>> childrenAttrsMap);
> >> >>>>>
> >> >>>>> - when a delegated component fires its event (e.g. the user clicks
> >> on an
> >> >>>>> AjaxLink) the event is handled by EDB's event handler. It extracts
> >> the
> >> >>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with
> >> the
> >> >>>>> specific attributes for the extracted inner element.
> >> >>>>>
> >> >>>>> Pros:
> >> >>>>>
> >> >>>>> - simple to use - just add EDB to a container component around
> your
> >> Ajax
> >> >>>>> heavy component (e.g. repeater with many Ajax behaviors). See the
> >> demo
> >> >>>>>
> >> >>>> app
> >> >>>>
> >> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
> >> https://issues.apache.org/jira/browse/WICKET-5267>
> >> >>>>>
> >> >>>>> -  faster JS execution
> >> >>>>> -- faster execution of the domready handler because there is just
> one
> >> >>>>> binding instead of N
> >> >>>>> -- faster reaction because the browser finds the event handler
> much
> >> >>>>>
> >> >>>> faster.
> >> >>>>
> >> >>>>> I wasn't able to prove this with numbers because there is no way
> to
> >> >>>>>
> >> >>>> detect
> >> >>>>
> >> >>>>> the 'start time', i.e. when the user makes the action. With JS the
> >> >>>>>
> >> >>>> earliest
> >> >>>>
> >> >>>>> point is when the browser has already looked up the event handler.
> >> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too.
> So
> >> my
> >> >>>>> reference that it is faster are the articles in the web and a use
> >> case
> >> >>>>> in
> >> >>>>> our application.
> >> >>>>>
> >> >>>>> Cons:
> >> >>>>>
> >> >>>>> - AEB#renderHead() needs to check whether there is EDB up in the
> >> >>>>>
> >> >>>> hierarchy
> >> >>>>
> >> >>>>> to be able to decide what to do.
> >> >>>>> This is ugly, I agree. But I see no other solution that will
> preserve
> >> >>>>> the
> >> >>>>> transparent usage of something like EDB and will not require a
> major
> >> >>>>> rewrite of user applications to be able to use event delegation.
> >> >>>>> -- there are some optimizations to lower the impact of the new
> >> checks:
> >> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
> >> >>>>> property
> >> >>>>> that prevents visiting the parent components and their behaviors
> for
> >> all
> >> >>>>> apps which do not use EDB
> >> >>>>> --- when EDB is bound it registers a metadata for its event type
> in
> >> the
> >> >>>>> page instance. This prevents visiting all behaviors of all parent
> >> >>>>> components
> >> >>>>>
> >> >>>>>
> >> >>>>> I have no more ideas how to further optimize it.
> >> >>>>>
> >> >>>>> Any feedback is welcome! Even if you have a completely different
> idea
> >> >>>>> how
> >> >>>>> to implement this functionality.
> >> >>>>>
> >> >>>>> Thanks for reading!
> >> >>>>>
> >> >>>>>
> >> >>
> >>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Igor Vaynberg <ig...@gmail.com>.
On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <mg...@apache.org> wrote:
> On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <ig...@gmail.com>wrote:
>
>> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <mg...@apache.org>
>> wrote:
>> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
>> >
>> >> Hi,
>> >>
>> >>
>> >> >The idea with plain JS solution I cannot visualize in my head yet.
>> >>
>> >> EventDelegatingBehavior is just a collector of JavaScript snippets. The
>> >> actual magic runs in the browser: a custom bubbling of events and
>> >> delegation to the actual behavior.
>> >> It should be possible to do this plain with JavaScript:
>> >>
>> >>   public class DelegatingAjax implements IAjax {
>> >>
>> >>     public ajax(IHeaderResponse response, Component component,
>> >> AjaxRequestAttributes attributes) {
>> >>       CharSequence ajaxAttributes = renderAjaxAttributes(**component,
>> >> attributes);
>> >>
>> >>
>> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
>> >> + ajaxAttributes + ");");
>> >>     }
>> >>   }
>> >>
>> >> This would be page-global though.
>> >
>> >
>> > This is an important detail!
>> > I'll consult with my frontend colleagues but so far I don't see problems.
>> >
>> > For every delegated component we can set special CSS class, e.g.
>> > 'wicket-delegated'.
>> > The binding will be: $(document).on('click', '.wicket-delegated',
>> > function(event) {....})
>> > i.e. we will take advantage of jQuery delegation/live support.
>> > This way even newly added items in the repeaters will be automatically
>> > supported.
>>
>>
>> this is partially on the right track, but there are still some
>> optimization that can be made.
>>
>> first, the ajax attributes need to be moved into a data attribute that
>> is written out on the tag. the final output of attaching a onclick
>> ajax behavior to a tag should end up looking like this:
>>
>> <a wicket:id="ajaxlink"
>> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
>>
>> (we will need to figure out how to encode ajax attributes into a string)
>>
>
> example:
> <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
>
> $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
>
> This works for valid JSON, but it doesn't for the enhancement we use - the
> functions for the call listeners.

i did say we need to figure out a way to encode it right above the example :)

>> then you can have the one global listener:
>>
>> $(document).on("click", function(e) {
>>
>
> The problem here is that using 'document' will make the things actually
> slower.
> We need to find a simple way to be able to bind on a parent component.
> In Sven's example - a table with many cells the most appropriate element is
> the <table> itself.

umm, why does it make things slower exactly? this has virtually no
overhead, events bubble up anyways...so where does the slowness come
from?

-igor

>
> In event-delegating-behavior branch I need to traverse the parent
> components and their behaviors to be able to find the appropriate parent.
> So we win some performance in JS execution but lose some in Java :-/
>
>    var element=$(this), attrs=element.attr("data-w-click");
>>    if (attrs&&!e.handledByWicket)
>>        Wicket.Ajax.call(attrs);
>>        e.handledByWicket=true; // if there are more handlers above, do
>> not double process the event - read below
>>    }
>> }
>>
>> the advantage here is that we only have one javascript listener that
>> needs to be registered.
>>
>> however, there are a few disadvantages:
>> * event propagation options wont work anymore, because the event has
>> to propagate all the way to the document in order to trigger.
>> * some libraries block events. for example if there is a panel with an
>> ajax link inside a third party modal window. the modal window lib may
>> prevent any clicks from propagating out of itself, which means the
>> handler on the document will never see them.
>>
>> we can sort of solve this by having a behavior that would write out
>> the listener above, but attached to the component not the document.
>>
>> that way, if we look at my example with the panel inside the modal,
>> the user can add this behavior to the panel that will be in the modal
>> and still be able to capture the event.
>>
>> this does, however, make troubleshooting more difficult. why didnt my
>> ajax event trigger? you will have to be a lot more aware about what
>> javascript you have in the dom.
>
>
>> i think a short term goal might be to move the ajax attributes into a
>> dom attribute and change our ajax code to simply say
>> Wicket.Ajax.bind("click", "component234");
>>
>
> see above (valid JSON)
>
> we can enrich the DOM:
> <a ... onsuccess="someScript">
> but I think this is a step back to Wicket 1.5 days (ajax decorators on
> strings, etc.)
>
>
>>
>> this will register the listener like above on the element directly. so
>> no delegation yet but cleaner javascript/html. also the browser doesnt
>> have to parse as much javascript, so it will be a bit speedier.
>>
>> potentially we can collect ids to further optimize js size:
>> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
>>
>> -igor
>>
>>
>> >
>> >
>> >>
>> >>
>> >> Sven
>> >>
>> >>
>> >>
>> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>> >>
>> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>> >>>
>> >>>  I think this is great - we have some tables now with a ton of JS
>> events
>> >>>> on
>> >>>> the child elements.  Just to clarify, will this make the rendered page
>> >>>> smaller since there will only be a single JS handler for the event for
>> >>>> the
>> >>>> container rather than N JS handlers?
>> >>>>
>> >>>>  At the moment all attributes for an inner element are preserved.
>> >>> 'e' (the event name), 'c' (the component markup id), pd (prevent
>> default),
>> >>> sp (stop propagation) can be removed because they are not really used.
>> >>> But every inner element can have its own call listeners, form
>> submitters
>> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they
>> have
>> >>> to be preserved.
>> >>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>> >>> table cells you will probably notice that they have their own
>> attributes.
>> >>>
>> >>>
>> >>>  Making it switchable (I think how Sven suggested) would be an
>> >>>> improvement -
>> >>>> we could leave it off by default, but provide a simple switch on a
>> >>>> per-container (or per-app) basis that would allow the dev to choose.
>> >>>>
>> >>>>  Yes, it looks as an improvement.
>> >>> Moving the current code to such implementation is easy.
>> >>> The idea with plain JS solution I cannot visualize in my head yet.
>> >>>
>> >>>
>> >>>  Regards
>> >>>>
>> >>>> Nick
>> >>>>
>> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
>> mgrigorov@apache.org
>> >>>>
>> >>>>> wrote:
>> >>>>> Hi,
>> >>>>>
>> >>>>> At https://github.com/apache/**wicket/compare/event-**
>> >>>>> delegating-behavioryou<
>> https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>> >>>>> may see the diff between master and event-delegating-behavior
>> branches.
>> >>>>>
>> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
>> >>>>>
>> >>>> EventDelegatingBehavior
>> >>>>
>> >>>>> (EDB), that suppresses the JS event binding for all
>> AjaxEventBehaviors
>> >>>>>
>> >>>> for
>> >>>>
>> >>>>> a given event type (click, submit, change, ...) in the children
>> >>>>>
>> >>>> components
>> >>>>
>> >>>>> of the host component of EDB.
>> >>>>>
>> >>>>> How EDB works:
>> >>>>>
>> >>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>> >>>>> item
>> >>>>> with JS snippet like:
>> >>>>> Wicket.Ajax.ajax(**attributesObject);
>> >>>>> In the new branch there is a check if some parent has EDB for the
>> event
>> >>>>> type of this AEB, and if there is such then the AEB "donates" its
>> >>>>> attributes to the EDB.
>> >>>>>
>> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>> >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
>> >>>>> edbAttributes,
>> >>>>> childrenAttrsMap);
>> >>>>>
>> >>>>> - when a delegated component fires its event (e.g. the user clicks
>> on an
>> >>>>> AjaxLink) the event is handled by EDB's event handler. It extracts
>> the
>> >>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with
>> the
>> >>>>> specific attributes for the extracted inner element.
>> >>>>>
>> >>>>> Pros:
>> >>>>>
>> >>>>> - simple to use - just add EDB to a container component around your
>> Ajax
>> >>>>> heavy component (e.g. repeater with many Ajax behaviors). See the
>> demo
>> >>>>>
>> >>>> app
>> >>>>
>> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
>> https://issues.apache.org/jira/browse/WICKET-5267>
>> >>>>>
>> >>>>> -  faster JS execution
>> >>>>> -- faster execution of the domready handler because there is just one
>> >>>>> binding instead of N
>> >>>>> -- faster reaction because the browser finds the event handler much
>> >>>>>
>> >>>> faster.
>> >>>>
>> >>>>> I wasn't able to prove this with numbers because there is no way to
>> >>>>>
>> >>>> detect
>> >>>>
>> >>>>> the 'start time', i.e. when the user makes the action. With JS the
>> >>>>>
>> >>>> earliest
>> >>>>
>> >>>>> point is when the browser has already looked up the event handler.
>> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So
>> my
>> >>>>> reference that it is faster are the articles in the web and a use
>> case
>> >>>>> in
>> >>>>> our application.
>> >>>>>
>> >>>>> Cons:
>> >>>>>
>> >>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>> >>>>>
>> >>>> hierarchy
>> >>>>
>> >>>>> to be able to decide what to do.
>> >>>>> This is ugly, I agree. But I see no other solution that will preserve
>> >>>>> the
>> >>>>> transparent usage of something like EDB and will not require a major
>> >>>>> rewrite of user applications to be able to use event delegation.
>> >>>>> -- there are some optimizations to lower the impact of the new
>> checks:
>> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
>> >>>>> property
>> >>>>> that prevents visiting the parent components and their behaviors for
>> all
>> >>>>> apps which do not use EDB
>> >>>>> --- when EDB is bound it registers a metadata for its event type in
>> the
>> >>>>> page instance. This prevents visiting all behaviors of all parent
>> >>>>> components
>> >>>>>
>> >>>>>
>> >>>>> I have no more ideas how to further optimize it.
>> >>>>>
>> >>>>> Any feedback is welcome! Even if you have a completely different idea
>> >>>>> how
>> >>>>> to implement this functionality.
>> >>>>>
>> >>>>> Thanks for reading!
>> >>>>>
>> >>>>>
>> >>
>>

Re: Ajax behavior for JavaScript event delegation

Posted by Sven Meier <sv...@meiers.net>.
I'm still sceptical about this feature:

IMHO event delegation is a delegate tool and you cannot insert it 
transparently: No one in the JavaScript world would try to do it that way.

When you start thinking about JavaScript performance of your event 
handlers you probably have a huge table in your markup. This can easily 
be solved with a custom event behavior (usually just click handlers) 
which can dissect which item was clicked on. No need to change anything 
in Wicket for that, instead you really get smaller html markup.
If you hope to be able to reuse complicated components in the table and 
just wrap event delegation magic around it ... that just doesn't work. 
For example Wicket's autoComplete and editableLabel won't benefit from 
the suggested solution, they use special event handling.

My proposal is to add an indirection into Wicket so that people can plug 
in their solution if they want to, e.g. something Martin has presented. 
But I wouldn't like to see this in core (yet).

Best regards
Sven


On 07/12/2013 10:50 AM, Martin Grigorov wrote:
> On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <ig...@gmail.com>wrote:
>
>> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <mg...@apache.org>
>> wrote:
>>> On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
>>>
>>>> Hi,
>>>>
>>>>
>>>>> The idea with plain JS solution I cannot visualize in my head yet.
>>>> EventDelegatingBehavior is just a collector of JavaScript snippets. The
>>>> actual magic runs in the browser: a custom bubbling of events and
>>>> delegation to the actual behavior.
>>>> It should be possible to do this plain with JavaScript:
>>>>
>>>>    public class DelegatingAjax implements IAjax {
>>>>
>>>>      public ajax(IHeaderResponse response, Component component,
>>>> AjaxRequestAttributes attributes) {
>>>>        CharSequence ajaxAttributes = renderAjaxAttributes(**component,
>>>> attributes);
>>>>
>>>>
>> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
>>>> + ajaxAttributes + ");");
>>>>      }
>>>>    }
>>>>
>>>> This would be page-global though.
>>>
>>> This is an important detail!
>>> I'll consult with my frontend colleagues but so far I don't see problems.
>>>
>>> For every delegated component we can set special CSS class, e.g.
>>> 'wicket-delegated'.
>>> The binding will be: $(document).on('click', '.wicket-delegated',
>>> function(event) {....})
>>> i.e. we will take advantage of jQuery delegation/live support.
>>> This way even newly added items in the repeaters will be automatically
>>> supported.
>>
>> this is partially on the right track, but there are still some
>> optimization that can be made.
>>
>> first, the ajax attributes need to be moved into a data attribute that
>> is written out on the tag. the final output of attaching a onclick
>> ajax behavior to a tag should end up looking like this:
>>
>> <a wicket:id="ajaxlink"
>> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
>>
>> (we will need to figure out how to encode ajax attributes into a string)
>>
> example:
> <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>
>
> $('#c23').data("w-attrs") === {u: "someUrl", m: "post"}
>
> This works for valid JSON, but it doesn't for the enhancement we use - the
> functions for the call listeners.
>
>
>> then you can have the one global listener:
>>
>> $(document).on("click", function(e) {
>>
> The problem here is that using 'document' will make the things actually
> slower.
> We need to find a simple way to be able to bind on a parent component.
> In Sven's example - a table with many cells the most appropriate element is
> the <table> itself.
>
> In event-delegating-behavior branch I need to traverse the parent
> components and their behaviors to be able to find the appropriate parent.
> So we win some performance in JS execution but lose some in Java :-/
>
>     var element=$(this), attrs=element.attr("data-w-click");
>>     if (attrs&&!e.handledByWicket)
>>         Wicket.Ajax.call(attrs);
>>         e.handledByWicket=true; // if there are more handlers above, do
>> not double process the event - read below
>>     }
>> }
>>
>> the advantage here is that we only have one javascript listener that
>> needs to be registered.
>>
>> however, there are a few disadvantages:
>> * event propagation options wont work anymore, because the event has
>> to propagate all the way to the document in order to trigger.
>> * some libraries block events. for example if there is a panel with an
>> ajax link inside a third party modal window. the modal window lib may
>> prevent any clicks from propagating out of itself, which means the
>> handler on the document will never see them.
>>
>> we can sort of solve this by having a behavior that would write out
>> the listener above, but attached to the component not the document.
>>
>> that way, if we look at my example with the panel inside the modal,
>> the user can add this behavior to the panel that will be in the modal
>> and still be able to capture the event.
>>
>> this does, however, make troubleshooting more difficult. why didnt my
>> ajax event trigger? you will have to be a lot more aware about what
>> javascript you have in the dom.
>
>> i think a short term goal might be to move the ajax attributes into a
>> dom attribute and change our ajax code to simply say
>> Wicket.Ajax.bind("click", "component234");
>>
> see above (valid JSON)
>
> we can enrich the DOM:
> <a ... onsuccess="someScript">
> but I think this is a step back to Wicket 1.5 days (ajax decorators on
> strings, etc.)
>
>
>> this will register the listener like above on the element directly. so
>> no delegation yet but cleaner javascript/html. also the browser doesnt
>> have to parse as much javascript, so it will be a bit speedier.
>>
>> potentially we can collect ids to further optimize js size:
>> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
>>
>> -igor
>>
>>
>>>
>>>>
>>>> Sven
>>>>
>>>>
>>>>
>>>> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>>>>
>>>>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>>>>>
>>>>>   I think this is great - we have some tables now with a ton of JS
>> events
>>>>>> on
>>>>>> the child elements.  Just to clarify, will this make the rendered page
>>>>>> smaller since there will only be a single JS handler for the event for
>>>>>> the
>>>>>> container rather than N JS handlers?
>>>>>>
>>>>>>   At the moment all attributes for an inner element are preserved.
>>>>> 'e' (the event name), 'c' (the component markup id), pd (prevent
>> default),
>>>>> sp (stop propagation) can be removed because they are not really used.
>>>>> But every inner element can have its own call listeners, form
>> submitters
>>>>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they
>> have
>>>>> to be preserved.
>>>>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>>>>> table cells you will probably notice that they have their own
>> attributes.
>>>>>
>>>>>   Making it switchable (I think how Sven suggested) would be an
>>>>>> improvement -
>>>>>> we could leave it off by default, but provide a simple switch on a
>>>>>> per-container (or per-app) basis that would allow the dev to choose.
>>>>>>
>>>>>>   Yes, it looks as an improvement.
>>>>> Moving the current code to such implementation is easy.
>>>>> The idea with plain JS solution I cannot visualize in my head yet.
>>>>>
>>>>>
>>>>>   Regards
>>>>>> Nick
>>>>>>
>>>>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
>> mgrigorov@apache.org
>>>>>>> wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> At https://github.com/apache/**wicket/compare/event-**
>>>>>>> delegating-behavioryou<
>> https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>>>>>>> may see the diff between master and event-delegating-behavior
>> branches.
>>>>>>> The latter provides a new AjaxEventBehavior (AEB) -
>>>>>>>
>>>>>> EventDelegatingBehavior
>>>>>>
>>>>>>> (EDB), that suppresses the JS event binding for all
>> AjaxEventBehaviors
>>>>>> for
>>>>>>
>>>>>>> a given event type (click, submit, change, ...) in the children
>>>>>>>
>>>>>> components
>>>>>>
>>>>>>> of the host component of EDB.
>>>>>>>
>>>>>>> How EDB works:
>>>>>>>
>>>>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>>>>>>> item
>>>>>>> with JS snippet like:
>>>>>>> Wicket.Ajax.ajax(**attributesObject);
>>>>>>> In the new branch there is a check if some parent has EDB for the
>> event
>>>>>>> type of this AEB, and if there is such then the AEB "donates" its
>>>>>>> attributes to the EDB.
>>>>>>>
>>>>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>>>>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
>>>>>>> edbAttributes,
>>>>>>> childrenAttrsMap);
>>>>>>>
>>>>>>> - when a delegated component fires its event (e.g. the user clicks
>> on an
>>>>>>> AjaxLink) the event is handled by EDB's event handler. It extracts
>> the
>>>>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with
>> the
>>>>>>> specific attributes for the extracted inner element.
>>>>>>>
>>>>>>> Pros:
>>>>>>>
>>>>>>> - simple to use - just add EDB to a container component around your
>> Ajax
>>>>>>> heavy component (e.g. repeater with many Ajax behaviors). See the
>> demo
>>>>>> app
>>>>>>
>>>>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
>> https://issues.apache.org/jira/browse/WICKET-5267>
>>>>>>> -  faster JS execution
>>>>>>> -- faster execution of the domready handler because there is just one
>>>>>>> binding instead of N
>>>>>>> -- faster reaction because the browser finds the event handler much
>>>>>>>
>>>>>> faster.
>>>>>>
>>>>>>> I wasn't able to prove this with numbers because there is no way to
>>>>>>>
>>>>>> detect
>>>>>>
>>>>>>> the 'start time', i.e. when the user makes the action. With JS the
>>>>>>>
>>>>>> earliest
>>>>>>
>>>>>>> point is when the browser has already looked up the event handler.
>>>>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So
>> my
>>>>>>> reference that it is faster are the articles in the web and a use
>> case
>>>>>>> in
>>>>>>> our application.
>>>>>>>
>>>>>>> Cons:
>>>>>>>
>>>>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>>>>>>>
>>>>>> hierarchy
>>>>>>
>>>>>>> to be able to decide what to do.
>>>>>>> This is ugly, I agree. But I see no other solution that will preserve
>>>>>>> the
>>>>>>> transparent usage of something like EDB and will not require a major
>>>>>>> rewrite of user applications to be able to use event delegation.
>>>>>>> -- there are some optimizations to lower the impact of the new
>> checks:
>>>>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
>>>>>>> property
>>>>>>> that prevents visiting the parent components and their behaviors for
>> all
>>>>>>> apps which do not use EDB
>>>>>>> --- when EDB is bound it registers a metadata for its event type in
>> the
>>>>>>> page instance. This prevents visiting all behaviors of all parent
>>>>>>> components
>>>>>>>
>>>>>>>
>>>>>>> I have no more ideas how to further optimize it.
>>>>>>>
>>>>>>> Any feedback is welcome! Even if you have a completely different idea
>>>>>>> how
>>>>>>> to implement this functionality.
>>>>>>>
>>>>>>> Thanks for reading!
>>>>>>>
>>>>>>>


Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <ig...@gmail.com>wrote:

> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <mg...@apache.org>
> wrote:
> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
> >
> >> Hi,
> >>
> >>
> >> >The idea with plain JS solution I cannot visualize in my head yet.
> >>
> >> EventDelegatingBehavior is just a collector of JavaScript snippets. The
> >> actual magic runs in the browser: a custom bubbling of events and
> >> delegation to the actual behavior.
> >> It should be possible to do this plain with JavaScript:
> >>
> >>   public class DelegatingAjax implements IAjax {
> >>
> >>     public ajax(IHeaderResponse response, Component component,
> >> AjaxRequestAttributes attributes) {
> >>       CharSequence ajaxAttributes = renderAjaxAttributes(**component,
> >> attributes);
> >>
> >>
> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
> >> + ajaxAttributes + ");");
> >>     }
> >>   }
> >>
> >> This would be page-global though.
> >
> >
> > This is an important detail!
> > I'll consult with my frontend colleagues but so far I don't see problems.
> >
> > For every delegated component we can set special CSS class, e.g.
> > 'wicket-delegated'.
> > The binding will be: $(document).on('click', '.wicket-delegated',
> > function(event) {....})
> > i.e. we will take advantage of jQuery delegation/live support.
> > This way even newly added items in the repeaters will be automatically
> > supported.
>
>
> this is partially on the right track, but there are still some
> optimization that can be made.
>
> first, the ajax attributes need to be moved into a data attribute that
> is written out on the tag. the final output of attaching a onclick
> ajax behavior to a tag should end up looking like this:
>
> <a wicket:id="ajaxlink"
> data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>
>
> (we will need to figure out how to encode ajax attributes into a string)
>

example:
<a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...>

$('#c23').data("w-attrs") === {u: "someUrl", m: "post"}

This works for valid JSON, but it doesn't for the enhancement we use - the
functions for the call listeners.


>
> then you can have the one global listener:
>
> $(document).on("click", function(e) {
>

The problem here is that using 'document' will make the things actually
slower.
We need to find a simple way to be able to bind on a parent component.
In Sven's example - a table with many cells the most appropriate element is
the <table> itself.

In event-delegating-behavior branch I need to traverse the parent
components and their behaviors to be able to find the appropriate parent.
So we win some performance in JS execution but lose some in Java :-/

   var element=$(this), attrs=element.attr("data-w-click");
>    if (attrs&&!e.handledByWicket)
>        Wicket.Ajax.call(attrs);
>        e.handledByWicket=true; // if there are more handlers above, do
> not double process the event - read below
>    }
> }
>
> the advantage here is that we only have one javascript listener that
> needs to be registered.
>
> however, there are a few disadvantages:
> * event propagation options wont work anymore, because the event has
> to propagate all the way to the document in order to trigger.
> * some libraries block events. for example if there is a panel with an
> ajax link inside a third party modal window. the modal window lib may
> prevent any clicks from propagating out of itself, which means the
> handler on the document will never see them.
>
> we can sort of solve this by having a behavior that would write out
> the listener above, but attached to the component not the document.
>
> that way, if we look at my example with the panel inside the modal,
> the user can add this behavior to the panel that will be in the modal
> and still be able to capture the event.
>
> this does, however, make troubleshooting more difficult. why didnt my
> ajax event trigger? you will have to be a lot more aware about what
> javascript you have in the dom.


> i think a short term goal might be to move the ajax attributes into a
> dom attribute and change our ajax code to simply say
> Wicket.Ajax.bind("click", "component234");
>

see above (valid JSON)

we can enrich the DOM:
<a ... onsuccess="someScript">
but I think this is a step back to Wicket 1.5 days (ajax decorators on
strings, etc.)


>
> this will register the listener like above on the element directly. so
> no delegation yet but cleaner javascript/html. also the browser doesnt
> have to parse as much javascript, so it will be a bit speedier.
>
> potentially we can collect ids to further optimize js size:
> Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);
>
> -igor
>
>
> >
> >
> >>
> >>
> >> Sven
> >>
> >>
> >>
> >> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
> >>
> >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
> >>>
> >>>  I think this is great - we have some tables now with a ton of JS
> events
> >>>> on
> >>>> the child elements.  Just to clarify, will this make the rendered page
> >>>> smaller since there will only be a single JS handler for the event for
> >>>> the
> >>>> container rather than N JS handlers?
> >>>>
> >>>>  At the moment all attributes for an inner element are preserved.
> >>> 'e' (the event name), 'c' (the component markup id), pd (prevent
> default),
> >>> sp (stop propagation) can be removed because they are not really used.
> >>> But every inner element can have its own call listeners, form
> submitters
> >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they
> have
> >>> to be preserved.
> >>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
> >>> table cells you will probably notice that they have their own
> attributes.
> >>>
> >>>
> >>>  Making it switchable (I think how Sven suggested) would be an
> >>>> improvement -
> >>>> we could leave it off by default, but provide a simple switch on a
> >>>> per-container (or per-app) basis that would allow the dev to choose.
> >>>>
> >>>>  Yes, it looks as an improvement.
> >>> Moving the current code to such implementation is easy.
> >>> The idea with plain JS solution I cannot visualize in my head yet.
> >>>
> >>>
> >>>  Regards
> >>>>
> >>>> Nick
> >>>>
> >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
> mgrigorov@apache.org
> >>>>
> >>>>> wrote:
> >>>>> Hi,
> >>>>>
> >>>>> At https://github.com/apache/**wicket/compare/event-**
> >>>>> delegating-behavioryou<
> https://github.com/apache/wicket/compare/event-delegating-behavioryou>
> >>>>> may see the diff between master and event-delegating-behavior
> branches.
> >>>>>
> >>>>> The latter provides a new AjaxEventBehavior (AEB) -
> >>>>>
> >>>> EventDelegatingBehavior
> >>>>
> >>>>> (EDB), that suppresses the JS event binding for all
> AjaxEventBehaviors
> >>>>>
> >>>> for
> >>>>
> >>>>> a given event type (click, submit, change, ...) in the children
> >>>>>
> >>>> components
> >>>>
> >>>>> of the host component of EDB.
> >>>>>
> >>>>> How EDB works:
> >>>>>
> >>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
> >>>>> item
> >>>>> with JS snippet like:
> >>>>> Wicket.Ajax.ajax(**attributesObject);
> >>>>> In the new branch there is a check if some parent has EDB for the
> event
> >>>>> type of this AEB, and if there is such then the AEB "donates" its
> >>>>> attributes to the EDB.
> >>>>>
> >>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
> >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
> >>>>> edbAttributes,
> >>>>> childrenAttrsMap);
> >>>>>
> >>>>> - when a delegated component fires its event (e.g. the user clicks
> on an
> >>>>> AjaxLink) the event is handled by EDB's event handler. It extracts
> the
> >>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with
> the
> >>>>> specific attributes for the extracted inner element.
> >>>>>
> >>>>> Pros:
> >>>>>
> >>>>> - simple to use - just add EDB to a container component around your
> Ajax
> >>>>> heavy component (e.g. repeater with many Ajax behaviors). See the
> demo
> >>>>>
> >>>> app
> >>>>
> >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<
> https://issues.apache.org/jira/browse/WICKET-5267>
> >>>>>
> >>>>> -  faster JS execution
> >>>>> -- faster execution of the domready handler because there is just one
> >>>>> binding instead of N
> >>>>> -- faster reaction because the browser finds the event handler much
> >>>>>
> >>>> faster.
> >>>>
> >>>>> I wasn't able to prove this with numbers because there is no way to
> >>>>>
> >>>> detect
> >>>>
> >>>>> the 'start time', i.e. when the user makes the action. With JS the
> >>>>>
> >>>> earliest
> >>>>
> >>>>> point is when the browser has already looked up the event handler.
> >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So
> my
> >>>>> reference that it is faster are the articles in the web and a use
> case
> >>>>> in
> >>>>> our application.
> >>>>>
> >>>>> Cons:
> >>>>>
> >>>>> - AEB#renderHead() needs to check whether there is EDB up in the
> >>>>>
> >>>> hierarchy
> >>>>
> >>>>> to be able to decide what to do.
> >>>>> This is ugly, I agree. But I see no other solution that will preserve
> >>>>> the
> >>>>> transparent usage of something like EDB and will not require a major
> >>>>> rewrite of user applications to be able to use event delegation.
> >>>>> -- there are some optimizations to lower the impact of the new
> checks:
> >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
> >>>>> property
> >>>>> that prevents visiting the parent components and their behaviors for
> all
> >>>>> apps which do not use EDB
> >>>>> --- when EDB is bound it registers a metadata for its event type in
> the
> >>>>> page instance. This prevents visiting all behaviors of all parent
> >>>>> components
> >>>>>
> >>>>>
> >>>>> I have no more ideas how to further optimize it.
> >>>>>
> >>>>> Any feedback is welcome! Even if you have a completely different idea
> >>>>> how
> >>>>> to implement this functionality.
> >>>>>
> >>>>> Thanks for reading!
> >>>>>
> >>>>>
> >>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Igor Vaynberg <ig...@gmail.com>.
On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <mg...@apache.org> wrote:
> On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
>
>> Hi,
>>
>>
>> >The idea with plain JS solution I cannot visualize in my head yet.
>>
>> EventDelegatingBehavior is just a collector of JavaScript snippets. The
>> actual magic runs in the browser: a custom bubbling of events and
>> delegation to the actual behavior.
>> It should be possible to do this plain with JavaScript:
>>
>>   public class DelegatingAjax implements IAjax {
>>
>>     public ajax(IHeaderResponse response, Component component,
>> AjaxRequestAttributes attributes) {
>>       CharSequence ajaxAttributes = renderAjaxAttributes(**component,
>> attributes);
>>
>> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
>> + ajaxAttributes + ");");
>>     }
>>   }
>>
>> This would be page-global though.
>
>
> This is an important detail!
> I'll consult with my frontend colleagues but so far I don't see problems.
>
> For every delegated component we can set special CSS class, e.g.
> 'wicket-delegated'.
> The binding will be: $(document).on('click', '.wicket-delegated',
> function(event) {....})
> i.e. we will take advantage of jQuery delegation/live support.
> This way even newly added items in the repeaters will be automatically
> supported.


this is partially on the right track, but there are still some
optimization that can be made.

first, the ajax attributes need to be moved into a data attribute that
is written out on the tag. the final output of attaching a onclick
ajax behavior to a tag should end up looking like this:

<a wicket:id="ajaxlink"
data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/>

(we will need to figure out how to encode ajax attributes into a string)

then you can have the one global listener:

$(document).on("click", function(e) {
   var element=$(this), attrs=element.attr("data-w-click");
   if (attrs&&!e.handledByWicket)
       Wicket.Ajax.call(attrs);
       e.handledByWicket=true; // if there are more handlers above, do
not double process the event - read below
   }
}

the advantage here is that we only have one javascript listener that
needs to be registered.

however, there are a few disadvantages:
* event propagation options wont work anymore, because the event has
to propagate all the way to the document in order to trigger.
* some libraries block events. for example if there is a panel with an
ajax link inside a third party modal window. the modal window lib may
prevent any clicks from propagating out of itself, which means the
handler on the document will never see them.

we can sort of solve this by having a behavior that would write out
the listener above, but attached to the component not the document.

that way, if we look at my example with the panel inside the modal,
the user can add this behavior to the panel that will be in the modal
and still be able to capture the event.

this does, however, make troubleshooting more difficult. why didnt my
ajax event trigger? you will have to be a lot more aware about what
javascript you have in the dom.

i think a short term goal might be to move the ajax attributes into a
dom attribute and change our ajax code to simply say
Wicket.Ajax.bind("click", "component234");

this will register the listener like above on the element directly. so
no delegation yet but cleaner javascript/html. also the browser doesnt
have to parse as much javascript, so it will be a bit speedier.

potentially we can collect ids to further optimize js size:
Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]);

-igor


>
>
>>
>>
>> Sven
>>
>>
>>
>> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>>
>>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>>>
>>>  I think this is great - we have some tables now with a ton of JS events
>>>> on
>>>> the child elements.  Just to clarify, will this make the rendered page
>>>> smaller since there will only be a single JS handler for the event for
>>>> the
>>>> container rather than N JS handlers?
>>>>
>>>>  At the moment all attributes for an inner element are preserved.
>>> 'e' (the event name), 'c' (the component markup id), pd (prevent default),
>>> sp (stop propagation) can be removed because they are not really used.
>>> But every inner element can have its own call listeners, form submitters
>>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they have
>>> to be preserved.
>>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>>> table cells you will probably notice that they have their own attributes.
>>>
>>>
>>>  Making it switchable (I think how Sven suggested) would be an
>>>> improvement -
>>>> we could leave it off by default, but provide a simple switch on a
>>>> per-container (or per-app) basis that would allow the dev to choose.
>>>>
>>>>  Yes, it looks as an improvement.
>>> Moving the current code to such implementation is easy.
>>> The idea with plain JS solution I cannot visualize in my head yet.
>>>
>>>
>>>  Regards
>>>>
>>>> Nick
>>>>
>>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mgrigorov@apache.org
>>>>
>>>>> wrote:
>>>>> Hi,
>>>>>
>>>>> At https://github.com/apache/**wicket/compare/event-**
>>>>> delegating-behavioryou<https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>>>>> may see the diff between master and event-delegating-behavior branches.
>>>>>
>>>>> The latter provides a new AjaxEventBehavior (AEB) -
>>>>>
>>>> EventDelegatingBehavior
>>>>
>>>>> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors
>>>>>
>>>> for
>>>>
>>>>> a given event type (click, submit, change, ...) in the children
>>>>>
>>>> components
>>>>
>>>>> of the host component of EDB.
>>>>>
>>>>> How EDB works:
>>>>>
>>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>>>>> item
>>>>> with JS snippet like:
>>>>> Wicket.Ajax.ajax(**attributesObject);
>>>>> In the new branch there is a check if some parent has EDB for the event
>>>>> type of this AEB, and if there is such then the AEB "donates" its
>>>>> attributes to the EDB.
>>>>>
>>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
>>>>> edbAttributes,
>>>>> childrenAttrsMap);
>>>>>
>>>>> - when a delegated component fires its event (e.g. the user clicks on an
>>>>> AjaxLink) the event is handled by EDB's event handler. It extracts the
>>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
>>>>> specific attributes for the extracted inner element.
>>>>>
>>>>> Pros:
>>>>>
>>>>> - simple to use - just add EDB to a container component around your Ajax
>>>>> heavy component (e.g. repeater with many Ajax behaviors). See the demo
>>>>>
>>>> app
>>>>
>>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<https://issues.apache.org/jira/browse/WICKET-5267>
>>>>>
>>>>> -  faster JS execution
>>>>> -- faster execution of the domready handler because there is just one
>>>>> binding instead of N
>>>>> -- faster reaction because the browser finds the event handler much
>>>>>
>>>> faster.
>>>>
>>>>> I wasn't able to prove this with numbers because there is no way to
>>>>>
>>>> detect
>>>>
>>>>> the 'start time', i.e. when the user makes the action. With JS the
>>>>>
>>>> earliest
>>>>
>>>>> point is when the browser has already looked up the event handler.
>>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
>>>>> reference that it is faster are the articles in the web and a use case
>>>>> in
>>>>> our application.
>>>>>
>>>>> Cons:
>>>>>
>>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>>>>>
>>>> hierarchy
>>>>
>>>>> to be able to decide what to do.
>>>>> This is ugly, I agree. But I see no other solution that will preserve
>>>>> the
>>>>> transparent usage of something like EDB and will not require a major
>>>>> rewrite of user applications to be able to use event delegation.
>>>>> -- there are some optimizations to lower the impact of the new checks:
>>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
>>>>> property
>>>>> that prevents visiting the parent components and their behaviors for all
>>>>> apps which do not use EDB
>>>>> --- when EDB is bound it registers a metadata for its event type in the
>>>>> page instance. This prevents visiting all behaviors of all parent
>>>>> components
>>>>>
>>>>>
>>>>> I have no more ideas how to further optimize it.
>>>>>
>>>>> Any feedback is welcome! Even if you have a completely different idea
>>>>> how
>>>>> to implement this functionality.
>>>>>
>>>>> Thanks for reading!
>>>>>
>>>>>
>>

Re: Ajax behavior for JavaScript event delegation

Posted by Cedric Gatay <ga...@gmail.com>.
I'd prefer using a data-delegated attribute than setting a special css
class on items. In my opinion, css classes should not reflect business
considerations, that's where data-* attributes are useful.

__
Cedric Gatay (@Cedric_Gatay <http://twitter.com/Cedric_Gatay>)
http://code-troopers.com | http://www.bloggure.info | http://cedric.gatay.fr


On Thu, Jul 11, 2013 at 5:08 PM, Sven Meier <sv...@meiers.net> wrote:

>  bind on document, and it was slow.
>>
>
> Sure, the higher you register a delegating event handler in the markup
> tree, the slower it is.
>
> The same holds for using event delegation on a table with a deeply nested
> cell structure.
>
> Sven
>
>
>
> On 07/11/2013 04:54 PM, Martin Grigorov wrote:
>
>> On Thu, Jul 11, 2013 at 5:22 PM, Martin Grigorov <mgrigorov@apache.org
>> >wrote:
>>
>>
>>>
>>> On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
>>>
>>>  Hi,
>>>>
>>>>
>>>>  The idea with plain JS solution I cannot visualize in my head yet.
>>>>>
>>>> EventDelegatingBehavior is just a collector of JavaScript snippets. The
>>>> actual magic runs in the browser: a custom bubbling of events and
>>>> delegation to the actual behavior.
>>>> It should be possible to do this plain with JavaScript:
>>>>
>>>>    public class DelegatingAjax implements IAjax {
>>>>
>>>>      public ajax(IHeaderResponse response, Component component,
>>>> AjaxRequestAttributes attributes) {
>>>>        CharSequence ajaxAttributes = renderAjaxAttributes(****
>>>> component,
>>>> attributes);
>>>>
>>>> response.render(****OnDomReadyHeaderItem.****
>>>> forScript("Wicket.Event.*****delegate*("
>>>> + ajaxAttributes + ");");
>>>>      }
>>>>    }
>>>>
>>>> This would be page-global though.
>>>>
>>>
>>> This is an important detail!
>>> I'll consult with my frontend colleagues but so far I don't see problems.
>>>
>>>  And they say this is a bad idea.
>> jQuery.live() (deprecated in 1.7.0) did the same, bind on document, and it
>> was slow.
>> See http://api.jquery.com/live/
>> Also see
>> http://www.ultimatewebtips.**com/why-jquery-live-is-a-bad-**
>> option-to-use/<http://www.ultimatewebtips.com/why-jquery-live-is-a-bad-option-to-use/>
>>
>>
>>  For every delegated component we can set special CSS class, e.g.
>>> 'wicket-delegated'.
>>> The binding will be: $(document).on('click', '.wicket-delegated',
>>> function(event) {....})
>>> i.e. we will take advantage of jQuery delegation/live support.
>>> This way even newly added items in the repeaters will be automatically
>>> supported.
>>>
>>>
>>>
>>>> Sven
>>>>
>>>>
>>>>
>>>> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>>>>
>>>>  On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>>>>>
>>>>>   I think this is great - we have some tables now with a ton of JS
>>>>> events
>>>>>
>>>>>> on
>>>>>> the child elements.  Just to clarify, will this make the rendered page
>>>>>> smaller since there will only be a single JS handler for the event for
>>>>>> the
>>>>>> container rather than N JS handlers?
>>>>>>
>>>>>>   At the moment all attributes for an inner element are preserved.
>>>>>>
>>>>> 'e' (the event name), 'c' (the component markup id), pd (prevent
>>>>> default),
>>>>> sp (stop propagation) can be removed because they are not really used.
>>>>> But every inner element can have its own call listeners, form
>>>>> submitters
>>>>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they
>>>>> have
>>>>> to be preserved.
>>>>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>>>>> table cells you will probably notice that they have their own
>>>>> attributes.
>>>>>
>>>>>
>>>>>   Making it switchable (I think how Sven suggested) would be an
>>>>>
>>>>>> improvement -
>>>>>> we could leave it off by default, but provide a simple switch on a
>>>>>> per-container (or per-app) basis that would allow the dev to choose.
>>>>>>
>>>>>>   Yes, it looks as an improvement.
>>>>>>
>>>>> Moving the current code to such implementation is easy.
>>>>> The idea with plain JS solution I cannot visualize in my head yet.
>>>>>
>>>>>
>>>>>   Regards
>>>>>
>>>>>> Nick
>>>>>>
>>>>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <
>>>>>> mgrigorov@apache.org
>>>>>>
>>>>>>  wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> At https://github.com/apache/****wicket/compare/event-**<https://github.com/apache/**wicket/compare/event-**>
>>>>>>> delegating-behavioryou<https:/**/github.com/apache/wicket/**
>>>>>>> compare/event-delegating-**behavioryou<https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>>>>>>> >
>>>>>>> may see the diff between master and event-delegating-behavior
>>>>>>> branches.
>>>>>>>
>>>>>>> The latter provides a new AjaxEventBehavior (AEB) -
>>>>>>>
>>>>>>>  EventDelegatingBehavior
>>>>>>
>>>>>>  (EDB), that suppresses the JS event binding for all
>>>>>>> AjaxEventBehaviors
>>>>>>>
>>>>>>>  for
>>>>>>
>>>>>>  a given event type (click, submit, change, ...) in the children
>>>>>>>
>>>>>>>  components
>>>>>>
>>>>>>  of the host component of EDB.
>>>>>>>
>>>>>>> How EDB works:
>>>>>>>
>>>>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>>>>>>> item
>>>>>>> with JS snippet like:
>>>>>>> Wicket.Ajax.ajax(****attributesObject);
>>>>>>> In the new branch there is a check if some parent has EDB for the
>>>>>>> event
>>>>>>> type of this AEB, and if there is such then the AEB "donates" its
>>>>>>> attributes to the EDB.
>>>>>>>
>>>>>>> - EventDelegatingBehavior#****getCallbackScript() renders :
>>>>>>> Wicket.Event.delegate('****edbComponentMarkupId', 'eventType',
>>>>>>> edbAttributes,
>>>>>>> childrenAttrsMap);
>>>>>>>
>>>>>>> - when a delegated component fires its event (e.g. the user clicks on
>>>>>>> an
>>>>>>> AjaxLink) the event is handled by EDB's event handler. It extracts
>>>>>>> the
>>>>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with
>>>>>>> the
>>>>>>> specific attributes for the extracted inner element.
>>>>>>>
>>>>>>> Pros:
>>>>>>>
>>>>>>> - simple to use - just add EDB to a container component around your
>>>>>>> Ajax
>>>>>>> heavy component (e.g. repeater with many Ajax behaviors). See the
>>>>>>> demo
>>>>>>>
>>>>>>>  app
>>>>>>
>>>>>>  at https://issues.apache.org/****jira/browse/WICKET-5267<https://issues.apache.org/**jira/browse/WICKET-5267>
>>>>>>> <https:**//issues.apache.org/jira/**browse/WICKET-5267<https://issues.apache.org/jira/browse/WICKET-5267>
>>>>>>> >
>>>>>>>
>>>>>>> -  faster JS execution
>>>>>>> -- faster execution of the domready handler because there is just one
>>>>>>> binding instead of N
>>>>>>> -- faster reaction because the browser finds the event handler much
>>>>>>>
>>>>>>>  faster.
>>>>>>
>>>>>>  I wasn't able to prove this with numbers because there is no way to
>>>>>>>
>>>>>>>  detect
>>>>>>
>>>>>>  the 'start time', i.e. when the user makes the action. With JS the
>>>>>>>
>>>>>>>  earliest
>>>>>>
>>>>>>  point is when the browser has already looked up the event handler.
>>>>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So
>>>>>>> my
>>>>>>> reference that it is faster are the articles in the web and a use
>>>>>>> case
>>>>>>> in
>>>>>>> our application.
>>>>>>>
>>>>>>> Cons:
>>>>>>>
>>>>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>>>>>>>
>>>>>>>  hierarchy
>>>>>>
>>>>>>  to be able to decide what to do.
>>>>>>> This is ugly, I agree. But I see no other solution that will preserve
>>>>>>> the
>>>>>>> transparent usage of something like EDB and will not require a major
>>>>>>> rewrite of user applications to be able to use event delegation.
>>>>>>> -- there are some optimizations to lower the impact of the new
>>>>>>> checks:
>>>>>>> --- a new setting (IAjaxSettings#****useEventDelegation) - a global
>>>>>>> property
>>>>>>> that prevents visiting the parent components and their behaviors for
>>>>>>> all
>>>>>>> apps which do not use EDB
>>>>>>> --- when EDB is bound it registers a metadata for its event type in
>>>>>>> the
>>>>>>> page instance. This prevents visiting all behaviors of all parent
>>>>>>> components
>>>>>>>
>>>>>>>
>>>>>>> I have no more ideas how to further optimize it.
>>>>>>>
>>>>>>> Any feedback is welcome! Even if you have a completely different idea
>>>>>>> how
>>>>>>> to implement this functionality.
>>>>>>>
>>>>>>> Thanks for reading!
>>>>>>>
>>>>>>>
>>>>>>>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Sven Meier <sv...@meiers.net>.
> bind on document, and it was slow.

Sure, the higher you register a delegating event handler in the markup tree, the slower it is.

The same holds for using event delegation on a table with a deeply nested cell structure.

Sven


On 07/11/2013 04:54 PM, Martin Grigorov wrote:
> On Thu, Jul 11, 2013 at 5:22 PM, Martin Grigorov <mg...@apache.org>wrote:
>
>>
>>
>> On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
>>
>>> Hi,
>>>
>>>
>>>> The idea with plain JS solution I cannot visualize in my head yet.
>>> EventDelegatingBehavior is just a collector of JavaScript snippets. The
>>> actual magic runs in the browser: a custom bubbling of events and
>>> delegation to the actual behavior.
>>> It should be possible to do this plain with JavaScript:
>>>
>>>    public class DelegatingAjax implements IAjax {
>>>
>>>      public ajax(IHeaderResponse response, Component component,
>>> AjaxRequestAttributes attributes) {
>>>        CharSequence ajaxAttributes = renderAjaxAttributes(**component,
>>> attributes);
>>>
>>> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
>>> + ajaxAttributes + ");");
>>>      }
>>>    }
>>>
>>> This would be page-global though.
>>
>> This is an important detail!
>> I'll consult with my frontend colleagues but so far I don't see problems.
>>
> And they say this is a bad idea.
> jQuery.live() (deprecated in 1.7.0) did the same, bind on document, and it
> was slow.
> See http://api.jquery.com/live/
> Also see
> http://www.ultimatewebtips.com/why-jquery-live-is-a-bad-option-to-use/
>
>
>> For every delegated component we can set special CSS class, e.g.
>> 'wicket-delegated'.
>> The binding will be: $(document).on('click', '.wicket-delegated',
>> function(event) {....})
>> i.e. we will take advantage of jQuery delegation/live support.
>> This way even newly added items in the repeaters will be automatically
>> supported.
>>
>>
>>>
>>> Sven
>>>
>>>
>>>
>>> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>>>
>>>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>>>>
>>>>   I think this is great - we have some tables now with a ton of JS events
>>>>> on
>>>>> the child elements.  Just to clarify, will this make the rendered page
>>>>> smaller since there will only be a single JS handler for the event for
>>>>> the
>>>>> container rather than N JS handlers?
>>>>>
>>>>>   At the moment all attributes for an inner element are preserved.
>>>> 'e' (the event name), 'c' (the component markup id), pd (prevent
>>>> default),
>>>> sp (stop propagation) can be removed because they are not really used.
>>>> But every inner element can have its own call listeners, form submitters
>>>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they
>>>> have
>>>> to be preserved.
>>>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>>>> table cells you will probably notice that they have their own attributes.
>>>>
>>>>
>>>>   Making it switchable (I think how Sven suggested) would be an
>>>>> improvement -
>>>>> we could leave it off by default, but provide a simple switch on a
>>>>> per-container (or per-app) basis that would allow the dev to choose.
>>>>>
>>>>>   Yes, it looks as an improvement.
>>>> Moving the current code to such implementation is easy.
>>>> The idea with plain JS solution I cannot visualize in my head yet.
>>>>
>>>>
>>>>   Regards
>>>>> Nick
>>>>>
>>>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mgrigorov@apache.org
>>>>>
>>>>>> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> At https://github.com/apache/**wicket/compare/event-**
>>>>>> delegating-behavioryou<https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>>>>>> may see the diff between master and event-delegating-behavior branches.
>>>>>>
>>>>>> The latter provides a new AjaxEventBehavior (AEB) -
>>>>>>
>>>>> EventDelegatingBehavior
>>>>>
>>>>>> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors
>>>>>>
>>>>> for
>>>>>
>>>>>> a given event type (click, submit, change, ...) in the children
>>>>>>
>>>>> components
>>>>>
>>>>>> of the host component of EDB.
>>>>>>
>>>>>> How EDB works:
>>>>>>
>>>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>>>>>> item
>>>>>> with JS snippet like:
>>>>>> Wicket.Ajax.ajax(**attributesObject);
>>>>>> In the new branch there is a check if some parent has EDB for the event
>>>>>> type of this AEB, and if there is such then the AEB "donates" its
>>>>>> attributes to the EDB.
>>>>>>
>>>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>>>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
>>>>>> edbAttributes,
>>>>>> childrenAttrsMap);
>>>>>>
>>>>>> - when a delegated component fires its event (e.g. the user clicks on
>>>>>> an
>>>>>> AjaxLink) the event is handled by EDB's event handler. It extracts the
>>>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
>>>>>> specific attributes for the extracted inner element.
>>>>>>
>>>>>> Pros:
>>>>>>
>>>>>> - simple to use - just add EDB to a container component around your
>>>>>> Ajax
>>>>>> heavy component (e.g. repeater with many Ajax behaviors). See the demo
>>>>>>
>>>>> app
>>>>>
>>>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<https://issues.apache.org/jira/browse/WICKET-5267>
>>>>>>
>>>>>> -  faster JS execution
>>>>>> -- faster execution of the domready handler because there is just one
>>>>>> binding instead of N
>>>>>> -- faster reaction because the browser finds the event handler much
>>>>>>
>>>>> faster.
>>>>>
>>>>>> I wasn't able to prove this with numbers because there is no way to
>>>>>>
>>>>> detect
>>>>>
>>>>>> the 'start time', i.e. when the user makes the action. With JS the
>>>>>>
>>>>> earliest
>>>>>
>>>>>> point is when the browser has already looked up the event handler.
>>>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
>>>>>> reference that it is faster are the articles in the web and a use case
>>>>>> in
>>>>>> our application.
>>>>>>
>>>>>> Cons:
>>>>>>
>>>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>>>>>>
>>>>> hierarchy
>>>>>
>>>>>> to be able to decide what to do.
>>>>>> This is ugly, I agree. But I see no other solution that will preserve
>>>>>> the
>>>>>> transparent usage of something like EDB and will not require a major
>>>>>> rewrite of user applications to be able to use event delegation.
>>>>>> -- there are some optimizations to lower the impact of the new checks:
>>>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
>>>>>> property
>>>>>> that prevents visiting the parent components and their behaviors for
>>>>>> all
>>>>>> apps which do not use EDB
>>>>>> --- when EDB is bound it registers a metadata for its event type in the
>>>>>> page instance. This prevents visiting all behaviors of all parent
>>>>>> components
>>>>>>
>>>>>>
>>>>>> I have no more ideas how to further optimize it.
>>>>>>
>>>>>> Any feedback is welcome! Even if you have a completely different idea
>>>>>> how
>>>>>> to implement this functionality.
>>>>>>
>>>>>> Thanks for reading!
>>>>>>
>>>>>>


Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
On Thu, Jul 11, 2013 at 5:22 PM, Martin Grigorov <mg...@apache.org>wrote:

>
>
>
> On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:
>
>> Hi,
>>
>>
>> >The idea with plain JS solution I cannot visualize in my head yet.
>>
>> EventDelegatingBehavior is just a collector of JavaScript snippets. The
>> actual magic runs in the browser: a custom bubbling of events and
>> delegation to the actual behavior.
>> It should be possible to do this plain with JavaScript:
>>
>>   public class DelegatingAjax implements IAjax {
>>
>>     public ajax(IHeaderResponse response, Component component,
>> AjaxRequestAttributes attributes) {
>>       CharSequence ajaxAttributes = renderAjaxAttributes(**component,
>> attributes);
>>
>> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
>> + ajaxAttributes + ");");
>>     }
>>   }
>>
>> This would be page-global though.
>
>
> This is an important detail!
> I'll consult with my frontend colleagues but so far I don't see problems.
>

And they say this is a bad idea.
jQuery.live() (deprecated in 1.7.0) did the same, bind on document, and it
was slow.
See http://api.jquery.com/live/
Also see
http://www.ultimatewebtips.com/why-jquery-live-is-a-bad-option-to-use/


>
> For every delegated component we can set special CSS class, e.g.
> 'wicket-delegated'.
> The binding will be: $(document).on('click', '.wicket-delegated',
> function(event) {....})
> i.e. we will take advantage of jQuery delegation/live support.
> This way even newly added items in the repeaters will be automatically
> supported.
>
>
>>
>>
>> Sven
>>
>>
>>
>> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>>
>>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>>>
>>>  I think this is great - we have some tables now with a ton of JS events
>>>> on
>>>> the child elements.  Just to clarify, will this make the rendered page
>>>> smaller since there will only be a single JS handler for the event for
>>>> the
>>>> container rather than N JS handlers?
>>>>
>>>>  At the moment all attributes for an inner element are preserved.
>>> 'e' (the event name), 'c' (the component markup id), pd (prevent
>>> default),
>>> sp (stop propagation) can be removed because they are not really used.
>>> But every inner element can have its own call listeners, form submitters
>>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they
>>> have
>>> to be preserved.
>>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>>> table cells you will probably notice that they have their own attributes.
>>>
>>>
>>>  Making it switchable (I think how Sven suggested) would be an
>>>> improvement -
>>>> we could leave it off by default, but provide a simple switch on a
>>>> per-container (or per-app) basis that would allow the dev to choose.
>>>>
>>>>  Yes, it looks as an improvement.
>>> Moving the current code to such implementation is easy.
>>> The idea with plain JS solution I cannot visualize in my head yet.
>>>
>>>
>>>  Regards
>>>>
>>>> Nick
>>>>
>>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mgrigorov@apache.org
>>>>
>>>>> wrote:
>>>>> Hi,
>>>>>
>>>>> At https://github.com/apache/**wicket/compare/event-**
>>>>> delegating-behavioryou<https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>>>>> may see the diff between master and event-delegating-behavior branches.
>>>>>
>>>>> The latter provides a new AjaxEventBehavior (AEB) -
>>>>>
>>>> EventDelegatingBehavior
>>>>
>>>>> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors
>>>>>
>>>> for
>>>>
>>>>> a given event type (click, submit, change, ...) in the children
>>>>>
>>>> components
>>>>
>>>>> of the host component of EDB.
>>>>>
>>>>> How EDB works:
>>>>>
>>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>>>>> item
>>>>> with JS snippet like:
>>>>> Wicket.Ajax.ajax(**attributesObject);
>>>>> In the new branch there is a check if some parent has EDB for the event
>>>>> type of this AEB, and if there is such then the AEB "donates" its
>>>>> attributes to the EDB.
>>>>>
>>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
>>>>> edbAttributes,
>>>>> childrenAttrsMap);
>>>>>
>>>>> - when a delegated component fires its event (e.g. the user clicks on
>>>>> an
>>>>> AjaxLink) the event is handled by EDB's event handler. It extracts the
>>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
>>>>> specific attributes for the extracted inner element.
>>>>>
>>>>> Pros:
>>>>>
>>>>> - simple to use - just add EDB to a container component around your
>>>>> Ajax
>>>>> heavy component (e.g. repeater with many Ajax behaviors). See the demo
>>>>>
>>>> app
>>>>
>>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<https://issues.apache.org/jira/browse/WICKET-5267>
>>>>>
>>>>> -  faster JS execution
>>>>> -- faster execution of the domready handler because there is just one
>>>>> binding instead of N
>>>>> -- faster reaction because the browser finds the event handler much
>>>>>
>>>> faster.
>>>>
>>>>> I wasn't able to prove this with numbers because there is no way to
>>>>>
>>>> detect
>>>>
>>>>> the 'start time', i.e. when the user makes the action. With JS the
>>>>>
>>>> earliest
>>>>
>>>>> point is when the browser has already looked up the event handler.
>>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
>>>>> reference that it is faster are the articles in the web and a use case
>>>>> in
>>>>> our application.
>>>>>
>>>>> Cons:
>>>>>
>>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>>>>>
>>>> hierarchy
>>>>
>>>>> to be able to decide what to do.
>>>>> This is ugly, I agree. But I see no other solution that will preserve
>>>>> the
>>>>> transparent usage of something like EDB and will not require a major
>>>>> rewrite of user applications to be able to use event delegation.
>>>>> -- there are some optimizations to lower the impact of the new checks:
>>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
>>>>> property
>>>>> that prevents visiting the parent components and their behaviors for
>>>>> all
>>>>> apps which do not use EDB
>>>>> --- when EDB is bound it registers a metadata for its event type in the
>>>>> page instance. This prevents visiting all behaviors of all parent
>>>>> components
>>>>>
>>>>>
>>>>> I have no more ideas how to further optimize it.
>>>>>
>>>>> Any feedback is welcome! Even if you have a completely different idea
>>>>> how
>>>>> to implement this functionality.
>>>>>
>>>>> Thanks for reading!
>>>>>
>>>>>
>>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <sv...@meiers.net> wrote:

> Hi,
>
>
> >The idea with plain JS solution I cannot visualize in my head yet.
>
> EventDelegatingBehavior is just a collector of JavaScript snippets. The
> actual magic runs in the browser: a custom bubbling of events and
> delegation to the actual behavior.
> It should be possible to do this plain with JavaScript:
>
>   public class DelegatingAjax implements IAjax {
>
>     public ajax(IHeaderResponse response, Component component,
> AjaxRequestAttributes attributes) {
>       CharSequence ajaxAttributes = renderAjaxAttributes(**component,
> attributes);
>
> response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*("
> + ajaxAttributes + ");");
>     }
>   }
>
> This would be page-global though.


This is an important detail!
I'll consult with my frontend colleagues but so far I don't see problems.

For every delegated component we can set special CSS class, e.g.
'wicket-delegated'.
The binding will be: $(document).on('click', '.wicket-delegated',
function(event) {....})
i.e. we will take advantage of jQuery delegation/live support.
This way even newly added items in the repeaters will be automatically
supported.


>
>
> Sven
>
>
>
> On 07/11/2013 03:40 PM, Martin Grigorov wrote:
>
>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>>
>>  I think this is great - we have some tables now with a ton of JS events
>>> on
>>> the child elements.  Just to clarify, will this make the rendered page
>>> smaller since there will only be a single JS handler for the event for
>>> the
>>> container rather than N JS handlers?
>>>
>>>  At the moment all attributes for an inner element are preserved.
>> 'e' (the event name), 'c' (the component markup id), pd (prevent default),
>> sp (stop propagation) can be removed because they are not really used.
>> But every inner element can have its own call listeners, form submitters
>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they have
>> to be preserved.
>> If you look in #updateAjaxAttributes() for your ajax behaviors in your
>> table cells you will probably notice that they have their own attributes.
>>
>>
>>  Making it switchable (I think how Sven suggested) would be an
>>> improvement -
>>> we could leave it off by default, but provide a simple switch on a
>>> per-container (or per-app) basis that would allow the dev to choose.
>>>
>>>  Yes, it looks as an improvement.
>> Moving the current code to such implementation is easy.
>> The idea with plain JS solution I cannot visualize in my head yet.
>>
>>
>>  Regards
>>>
>>> Nick
>>>
>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mgrigorov@apache.org
>>>
>>>> wrote:
>>>> Hi,
>>>>
>>>> At https://github.com/apache/**wicket/compare/event-**
>>>> delegating-behavioryou<https://github.com/apache/wicket/compare/event-delegating-behavioryou>
>>>> may see the diff between master and event-delegating-behavior branches.
>>>>
>>>> The latter provides a new AjaxEventBehavior (AEB) -
>>>>
>>> EventDelegatingBehavior
>>>
>>>> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors
>>>>
>>> for
>>>
>>>> a given event type (click, submit, change, ...) in the children
>>>>
>>> components
>>>
>>>> of the host component of EDB.
>>>>
>>>> How EDB works:
>>>>
>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header
>>>> item
>>>> with JS snippet like:
>>>> Wicket.Ajax.ajax(**attributesObject);
>>>> In the new branch there is a check if some parent has EDB for the event
>>>> type of this AEB, and if there is such then the AEB "donates" its
>>>> attributes to the EDB.
>>>>
>>>> - EventDelegatingBehavior#**getCallbackScript() renders :
>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType',
>>>> edbAttributes,
>>>> childrenAttrsMap);
>>>>
>>>> - when a delegated component fires its event (e.g. the user clicks on an
>>>> AjaxLink) the event is handled by EDB's event handler. It extracts the
>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
>>>> specific attributes for the extracted inner element.
>>>>
>>>> Pros:
>>>>
>>>> - simple to use - just add EDB to a container component around your Ajax
>>>> heavy component (e.g. repeater with many Ajax behaviors). See the demo
>>>>
>>> app
>>>
>>>> at https://issues.apache.org/**jira/browse/WICKET-5267<https://issues.apache.org/jira/browse/WICKET-5267>
>>>>
>>>> -  faster JS execution
>>>> -- faster execution of the domready handler because there is just one
>>>> binding instead of N
>>>> -- faster reaction because the browser finds the event handler much
>>>>
>>> faster.
>>>
>>>> I wasn't able to prove this with numbers because there is no way to
>>>>
>>> detect
>>>
>>>> the 'start time', i.e. when the user makes the action. With JS the
>>>>
>>> earliest
>>>
>>>> point is when the browser has already looked up the event handler.
>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
>>>> reference that it is faster are the articles in the web and a use case
>>>> in
>>>> our application.
>>>>
>>>> Cons:
>>>>
>>>> - AEB#renderHead() needs to check whether there is EDB up in the
>>>>
>>> hierarchy
>>>
>>>> to be able to decide what to do.
>>>> This is ugly, I agree. But I see no other solution that will preserve
>>>> the
>>>> transparent usage of something like EDB and will not require a major
>>>> rewrite of user applications to be able to use event delegation.
>>>> -- there are some optimizations to lower the impact of the new checks:
>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global
>>>> property
>>>> that prevents visiting the parent components and their behaviors for all
>>>> apps which do not use EDB
>>>> --- when EDB is bound it registers a metadata for its event type in the
>>>> page instance. This prevents visiting all behaviors of all parent
>>>> components
>>>>
>>>>
>>>> I have no more ideas how to further optimize it.
>>>>
>>>> Any feedback is welcome! Even if you have a completely different idea
>>>> how
>>>> to implement this functionality.
>>>>
>>>> Thanks for reading!
>>>>
>>>>
>

Re: Ajax behavior for JavaScript event delegation

Posted by Sven Meier <sv...@meiers.net>.
Hi,

 >The idea with plain JS solution I cannot visualize in my head yet.

EventDelegatingBehavior is just a collector of JavaScript snippets. The 
actual magic runs in the browser: a custom bubbling of events and 
delegation to the actual behavior.
It should be possible to do this plain with JavaScript:

   public class DelegatingAjax implements IAjax {
     public ajax(IHeaderResponse response, Component component, 
AjaxRequestAttributes attributes) {
       CharSequence ajaxAttributes = renderAjaxAttributes(component, 
attributes);

response.render(OnDomReadyHeaderItem.forScript("Wicket.Event.*delegate*(" + 
ajaxAttributes + ");");
     }
   }

This would be page-global though.

Sven


On 07/11/2013 03:40 PM, Martin Grigorov wrote:
> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:
>
>> I think this is great - we have some tables now with a ton of JS events on
>> the child elements.  Just to clarify, will this make the rendered page
>> smaller since there will only be a single JS handler for the event for the
>> container rather than N JS handlers?
>>
> At the moment all attributes for an inner element are preserved.
> 'e' (the event name), 'c' (the component markup id), pd (prevent default),
> sp (stop propagation) can be removed because they are not really used.
> But every inner element can have its own call listeners, form submitters
> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they have
> to be preserved.
> If you look in #updateAjaxAttributes() for your ajax behaviors in your
> table cells you will probably notice that they have their own attributes.
>
>
>> Making it switchable (I think how Sven suggested) would be an improvement -
>> we could leave it off by default, but provide a simple switch on a
>> per-container (or per-app) basis that would allow the dev to choose.
>>
> Yes, it looks as an improvement.
> Moving the current code to such implementation is easy.
> The idea with plain JS solution I cannot visualize in my head yet.
>
>
>> Regards
>>
>> Nick
>>
>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mgrigorov@apache.org
>>> wrote:
>>> Hi,
>>>
>>> At https://github.com/apache/wicket/compare/event-delegating-behavioryou
>>> may see the diff between master and event-delegating-behavior branches.
>>>
>>> The latter provides a new AjaxEventBehavior (AEB) -
>> EventDelegatingBehavior
>>> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors
>> for
>>> a given event type (click, submit, change, ...) in the children
>> components
>>> of the host component of EDB.
>>>
>>> How EDB works:
>>>
>>> - until now AjaxEventBehavior#renderHead() renders ondomready header item
>>> with JS snippet like:
>>> Wicket.Ajax.ajax(attributesObject);
>>> In the new branch there is a check if some parent has EDB for the event
>>> type of this AEB, and if there is such then the AEB "donates" its
>>> attributes to the EDB.
>>>
>>> - EventDelegatingBehavior#getCallbackScript() renders :
>>> Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
>>> childrenAttrsMap);
>>>
>>> - when a delegated component fires its event (e.g. the user clicks on an
>>> AjaxLink) the event is handled by EDB's event handler. It extracts the
>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
>>> specific attributes for the extracted inner element.
>>>
>>> Pros:
>>>
>>> - simple to use - just add EDB to a container component around your Ajax
>>> heavy component (e.g. repeater with many Ajax behaviors). See the demo
>> app
>>> at https://issues.apache.org/jira/browse/WICKET-5267
>>>
>>> -  faster JS execution
>>> -- faster execution of the domready handler because there is just one
>>> binding instead of N
>>> -- faster reaction because the browser finds the event handler much
>> faster.
>>> I wasn't able to prove this with numbers because there is no way to
>> detect
>>> the 'start time', i.e. when the user makes the action. With JS the
>> earliest
>>> point is when the browser has already looked up the event handler.
>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
>>> reference that it is faster are the articles in the web and a use case in
>>> our application.
>>>
>>> Cons:
>>>
>>> - AEB#renderHead() needs to check whether there is EDB up in the
>> hierarchy
>>> to be able to decide what to do.
>>> This is ugly, I agree. But I see no other solution that will preserve the
>>> transparent usage of something like EDB and will not require a major
>>> rewrite of user applications to be able to use event delegation.
>>> -- there are some optimizations to lower the impact of the new checks:
>>> --- a new setting (IAjaxSettings#useEventDelegation) - a global property
>>> that prevents visiting the parent components and their behaviors for all
>>> apps which do not use EDB
>>> --- when EDB is bound it registers a metadata for its event type in the
>>> page instance. This prevents visiting all behaviors of all parent
>>> components
>>>
>>>
>>> I have no more ideas how to further optimize it.
>>>
>>> Any feedback is welcome! Even if you have a completely different idea how
>>> to implement this functionality.
>>>
>>> Thanks for reading!
>>>


Re: Ajax behavior for JavaScript event delegation

Posted by Martin Grigorov <mg...@apache.org>.
On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nb...@gmail.com> wrote:

> I think this is great - we have some tables now with a ton of JS events on
> the child elements.  Just to clarify, will this make the rendered page
> smaller since there will only be a single JS handler for the event for the
> container rather than N JS handlers?
>

At the moment all attributes for an inner element are preserved.
'e' (the event name), 'c' (the component markup id), pd (prevent default),
sp (stop propagation) can be removed because they are not really used.
But every inner element can have its own call listeners, form submitters
can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they have
to be preserved.
If you look in #updateAjaxAttributes() for your ajax behaviors in your
table cells you will probably notice that they have their own attributes.


>
> Making it switchable (I think how Sven suggested) would be an improvement -
> we could leave it off by default, but provide a simple switch on a
> per-container (or per-app) basis that would allow the dev to choose.
>

Yes, it looks as an improvement.
Moving the current code to such implementation is easy.
The idea with plain JS solution I cannot visualize in my head yet.


>
> Regards
>
> Nick
>
> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mgrigorov@apache.org
> >wrote:
>
> > Hi,
> >
> > At https://github.com/apache/wicket/compare/event-delegating-behavioryou
> > may see the diff between master and event-delegating-behavior branches.
> >
> > The latter provides a new AjaxEventBehavior (AEB) -
> EventDelegatingBehavior
> > (EDB), that suppresses the JS event binding for all AjaxEventBehaviors
> for
> > a given event type (click, submit, change, ...) in the children
> components
> > of the host component of EDB.
> >
> > How EDB works:
> >
> > - until now AjaxEventBehavior#renderHead() renders ondomready header item
> > with JS snippet like:
> > Wicket.Ajax.ajax(attributesObject);
> > In the new branch there is a check if some parent has EDB for the event
> > type of this AEB, and if there is such then the AEB "donates" its
> > attributes to the EDB.
> >
> > - EventDelegatingBehavior#getCallbackScript() renders :
> > Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
> > childrenAttrsMap);
> >
> > - when a delegated component fires its event (e.g. the user clicks on an
> > AjaxLink) the event is handled by EDB's event handler. It extracts the
> > markupId of the inner HTML element and fires Wicket.Ajax.Call with the
> > specific attributes for the extracted inner element.
> >
> > Pros:
> >
> > - simple to use - just add EDB to a container component around your Ajax
> > heavy component (e.g. repeater with many Ajax behaviors). See the demo
> app
> > at https://issues.apache.org/jira/browse/WICKET-5267
> >
> > -  faster JS execution
> > -- faster execution of the domready handler because there is just one
> > binding instead of N
> > -- faster reaction because the browser finds the event handler much
> faster.
> > I wasn't able to prove this with numbers because there is no way to
> detect
> > the 'start time', i.e. when the user makes the action. With JS the
> earliest
> > point is when the browser has already looked up the event handler.
> > Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
> > reference that it is faster are the articles in the web and a use case in
> > our application.
> >
> > Cons:
> >
> > - AEB#renderHead() needs to check whether there is EDB up in the
> hierarchy
> > to be able to decide what to do.
> > This is ugly, I agree. But I see no other solution that will preserve the
> > transparent usage of something like EDB and will not require a major
> > rewrite of user applications to be able to use event delegation.
> > -- there are some optimizations to lower the impact of the new checks:
> > --- a new setting (IAjaxSettings#useEventDelegation) - a global property
> > that prevents visiting the parent components and their behaviors for all
> > apps which do not use EDB
> > --- when EDB is bound it registers a metadata for its event type in the
> > page instance. This prevents visiting all behaviors of all parent
> > components
> >
> >
> > I have no more ideas how to further optimize it.
> >
> > Any feedback is welcome! Even if you have a completely different idea how
> > to implement this functionality.
> >
> > Thanks for reading!
> >
>

Re: Ajax behavior for JavaScript event delegation

Posted by Sven Meier <sv...@meiers.net>.
> Just to clarify, will this make the rendered page smaller ... ?

Very slightly only :(

Sven


On 07/11/2013 03:30 PM, Nick Pratt wrote:
> I think this is great - we have some tables now with a ton of JS events on
> the child elements.  Just to clarify, will this make the rendered page
> smaller since there will only be a single JS handler for the event for the
> container rather than N JS handlers?
>
> Making it switchable (I think how Sven suggested) would be an improvement -
> we could leave it off by default, but provide a simple switch on a
> per-container (or per-app) basis that would allow the dev to choose.
>
> Regards
>
> Nick
>
> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mg...@apache.org>wrote:
>
>> Hi,
>>
>> At https://github.com/apache/wicket/compare/event-delegating-behavior you
>> may see the diff between master and event-delegating-behavior branches.
>>
>> The latter provides a new AjaxEventBehavior (AEB) - EventDelegatingBehavior
>> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors for
>> a given event type (click, submit, change, ...) in the children components
>> of the host component of EDB.
>>
>> How EDB works:
>>
>> - until now AjaxEventBehavior#renderHead() renders ondomready header item
>> with JS snippet like:
>> Wicket.Ajax.ajax(attributesObject);
>> In the new branch there is a check if some parent has EDB for the event
>> type of this AEB, and if there is such then the AEB "donates" its
>> attributes to the EDB.
>>
>> - EventDelegatingBehavior#getCallbackScript() renders :
>> Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
>> childrenAttrsMap);
>>
>> - when a delegated component fires its event (e.g. the user clicks on an
>> AjaxLink) the event is handled by EDB's event handler. It extracts the
>> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
>> specific attributes for the extracted inner element.
>>
>> Pros:
>>
>> - simple to use - just add EDB to a container component around your Ajax
>> heavy component (e.g. repeater with many Ajax behaviors). See the demo app
>> at https://issues.apache.org/jira/browse/WICKET-5267
>>
>> -  faster JS execution
>> -- faster execution of the domready handler because there is just one
>> binding instead of N
>> -- faster reaction because the browser finds the event handler much faster.
>> I wasn't able to prove this with numbers because there is no way to detect
>> the 'start time', i.e. when the user makes the action. With JS the earliest
>> point is when the browser has already looked up the event handler.
>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
>> reference that it is faster are the articles in the web and a use case in
>> our application.
>>
>> Cons:
>>
>> - AEB#renderHead() needs to check whether there is EDB up in the hierarchy
>> to be able to decide what to do.
>> This is ugly, I agree. But I see no other solution that will preserve the
>> transparent usage of something like EDB and will not require a major
>> rewrite of user applications to be able to use event delegation.
>> -- there are some optimizations to lower the impact of the new checks:
>> --- a new setting (IAjaxSettings#useEventDelegation) - a global property
>> that prevents visiting the parent components and their behaviors for all
>> apps which do not use EDB
>> --- when EDB is bound it registers a metadata for its event type in the
>> page instance. This prevents visiting all behaviors of all parent
>> components
>>
>>
>> I have no more ideas how to further optimize it.
>>
>> Any feedback is welcome! Even if you have a completely different idea how
>> to implement this functionality.
>>
>> Thanks for reading!
>>


Re: Ajax behavior for JavaScript event delegation

Posted by Nick Pratt <nb...@gmail.com>.
I think this is great - we have some tables now with a ton of JS events on
the child elements.  Just to clarify, will this make the rendered page
smaller since there will only be a single JS handler for the event for the
container rather than N JS handlers?

Making it switchable (I think how Sven suggested) would be an improvement -
we could leave it off by default, but provide a simple switch on a
per-container (or per-app) basis that would allow the dev to choose.

Regards

Nick

On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov <mg...@apache.org>wrote:

> Hi,
>
> At https://github.com/apache/wicket/compare/event-delegating-behavior you
> may see the diff between master and event-delegating-behavior branches.
>
> The latter provides a new AjaxEventBehavior (AEB) - EventDelegatingBehavior
> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors for
> a given event type (click, submit, change, ...) in the children components
> of the host component of EDB.
>
> How EDB works:
>
> - until now AjaxEventBehavior#renderHead() renders ondomready header item
> with JS snippet like:
> Wicket.Ajax.ajax(attributesObject);
> In the new branch there is a check if some parent has EDB for the event
> type of this AEB, and if there is such then the AEB "donates" its
> attributes to the EDB.
>
> - EventDelegatingBehavior#getCallbackScript() renders :
> Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
> childrenAttrsMap);
>
> - when a delegated component fires its event (e.g. the user clicks on an
> AjaxLink) the event is handled by EDB's event handler. It extracts the
> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
> specific attributes for the extracted inner element.
>
> Pros:
>
> - simple to use - just add EDB to a container component around your Ajax
> heavy component (e.g. repeater with many Ajax behaviors). See the demo app
> at https://issues.apache.org/jira/browse/WICKET-5267
>
> -  faster JS execution
> -- faster execution of the domready handler because there is just one
> binding instead of N
> -- faster reaction because the browser finds the event handler much faster.
> I wasn't able to prove this with numbers because there is no way to detect
> the 'start time', i.e. when the user makes the action. With JS the earliest
> point is when the browser has already looked up the event handler.
> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
> reference that it is faster are the articles in the web and a use case in
> our application.
>
> Cons:
>
> - AEB#renderHead() needs to check whether there is EDB up in the hierarchy
> to be able to decide what to do.
> This is ugly, I agree. But I see no other solution that will preserve the
> transparent usage of something like EDB and will not require a major
> rewrite of user applications to be able to use event delegation.
> -- there are some optimizations to lower the impact of the new checks:
> --- a new setting (IAjaxSettings#useEventDelegation) - a global property
> that prevents visiting the parent components and their behaviors for all
> apps which do not use EDB
> --- when EDB is bound it registers a metadata for its event type in the
> page instance. This prevents visiting all behaviors of all parent
> components
>
>
> I have no more ideas how to further optimize it.
>
> Any feedback is welcome! Even if you have a completely different idea how
> to implement this functionality.
>
> Thanks for reading!
>

Re: Ajax behavior for JavaScript event delegation

Posted by Cedric Gatay <ga...@gmail.com>.
I find this is a great idea. We often use data tables with a lot of ajax
behaviors, with this kind of behavior it should speed it up. I will try to
test it in the coming days.

__
Cedric Gatay (@Cedric_Gatay <http://twitter.com/Cedric_Gatay>)
http://code-troopers.com | http://www.bloggure.info | http://cedric.gatay.fr


On Thu, Jul 11, 2013 at 10:59 AM, Martin Grigorov <mg...@apache.org>wrote:

> Hi,
>
> At https://github.com/apache/wicket/compare/event-delegating-behavior you
> may see the diff between master and event-delegating-behavior branches.
>
> The latter provides a new AjaxEventBehavior (AEB) - EventDelegatingBehavior
> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors for
> a given event type (click, submit, change, ...) in the children components
> of the host component of EDB.
>
> How EDB works:
>
> - until now AjaxEventBehavior#renderHead() renders ondomready header item
> with JS snippet like:
> Wicket.Ajax.ajax(attributesObject);
> In the new branch there is a check if some parent has EDB for the event
> type of this AEB, and if there is such then the AEB "donates" its
> attributes to the EDB.
>
> - EventDelegatingBehavior#getCallbackScript() renders :
> Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
> childrenAttrsMap);
>
> - when a delegated component fires its event (e.g. the user clicks on an
> AjaxLink) the event is handled by EDB's event handler. It extracts the
> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
> specific attributes for the extracted inner element.
>
> Pros:
>
> - simple to use - just add EDB to a container component around your Ajax
> heavy component (e.g. repeater with many Ajax behaviors). See the demo app
> at https://issues.apache.org/jira/browse/WICKET-5267
>
> -  faster JS execution
> -- faster execution of the domready handler because there is just one
> binding instead of N
> -- faster reaction because the browser finds the event handler much faster.
> I wasn't able to prove this with numbers because there is no way to detect
> the 'start time', i.e. when the user makes the action. With JS the earliest
> point is when the browser has already looked up the event handler.
> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
> reference that it is faster are the articles in the web and a use case in
> our application.
>
> Cons:
>
> - AEB#renderHead() needs to check whether there is EDB up in the hierarchy
> to be able to decide what to do.
> This is ugly, I agree. But I see no other solution that will preserve the
> transparent usage of something like EDB and will not require a major
> rewrite of user applications to be able to use event delegation.
> -- there are some optimizations to lower the impact of the new checks:
> --- a new setting (IAjaxSettings#useEventDelegation) - a global property
> that prevents visiting the parent components and their behaviors for all
> apps which do not use EDB
> --- when EDB is bound it registers a metadata for its event type in the
> page instance. This prevents visiting all behaviors of all parent
> components
>
>
> I have no more ideas how to further optimize it.
>
> Any feedback is welcome! Even if you have a completely different idea how
> to implement this functionality.
>
> Thanks for reading!
>

Re: Ajax behavior for JavaScript event delegation

Posted by Sven Meier <sv...@meiers.net>.
 > Thanks for reading!

How about making the whole thing a little more 'pluggable'?

     // AjaxEventBehavior
     public void renderHead(final Component component, final 
IHeaderResponse response)
     {
         super.renderHead(component, response);

         if (component.isEnabledInHierarchy())
         {
component.getApplication().getJavaScriptLibrarySettings().getAjax().ajax(response, 
component, getAttributes());
         }
     }

   public class DefaultAjax implements IAjax {
     public ajax(IHeaderResponse response, Component component, 
AjaxRequestAttributes attributes) {
       CharSequence ajaxAttributes = renderAjaxAttributes(component, 
attributes);

response.render(OnDomReadyHeaderItem.forScript("Wicket.Ajax.ajax(" + 
ajaxAttributes + ");");
     }
   }

Then an alternative implementation could do as much delegation as it 
wants to. For example we could build a pure JavaScript solution that 
works without an EventDelegatingBehavior.

Sven


On 07/11/2013 10:59 AM, Martin Grigorov wrote:
> Hi,
>
> At https://github.com/apache/wicket/compare/event-delegating-behavior you
> may see the diff between master and event-delegating-behavior branches.
>
> The latter provides a new AjaxEventBehavior (AEB) - EventDelegatingBehavior
> (EDB), that suppresses the JS event binding for all AjaxEventBehaviors for
> a given event type (click, submit, change, ...) in the children components
> of the host component of EDB.
>
> How EDB works:
>
> - until now AjaxEventBehavior#renderHead() renders ondomready header item
> with JS snippet like:
> Wicket.Ajax.ajax(attributesObject);
> In the new branch there is a check if some parent has EDB for the event
> type of this AEB, and if there is such then the AEB "donates" its
> attributes to the EDB.
>
> - EventDelegatingBehavior#getCallbackScript() renders :
> Wicket.Event.delegate('edbComponentMarkupId', 'eventType', edbAttributes,
> childrenAttrsMap);
>
> - when a delegated component fires its event (e.g. the user clicks on an
> AjaxLink) the event is handled by EDB's event handler. It extracts the
> markupId of the inner HTML element and fires Wicket.Ajax.Call with the
> specific attributes for the extracted inner element.
>
> Pros:
>
> - simple to use - just add EDB to a container component around your Ajax
> heavy component (e.g. repeater with many Ajax behaviors). See the demo app
> at https://issues.apache.org/jira/browse/WICKET-5267
>
> -  faster JS execution
> -- faster execution of the domready handler because there is just one
> binding instead of N
> -- faster reaction because the browser finds the event handler much faster.
> I wasn't able to prove this with numbers because there is no way to detect
> the 'start time', i.e. when the user makes the action. With JS the earliest
> point is when the browser has already looked up the event handler.
> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So my
> reference that it is faster are the articles in the web and a use case in
> our application.
>
> Cons:
>
> - AEB#renderHead() needs to check whether there is EDB up in the hierarchy
> to be able to decide what to do.
> This is ugly, I agree. But I see no other solution that will preserve the
> transparent usage of something like EDB and will not require a major
> rewrite of user applications to be able to use event delegation.
> -- there are some optimizations to lower the impact of the new checks:
> --- a new setting (IAjaxSettings#useEventDelegation) - a global property
> that prevents visiting the parent components and their behaviors for all
> apps which do not use EDB
> --- when EDB is bound it registers a metadata for its event type in the
> page instance. This prevents visiting all behaviors of all parent components
>
>
> I have no more ideas how to further optimize it.
>
> Any feedback is welcome! Even if you have a completely different idea how
> to implement this functionality.
>
> Thanks for reading!
>