On Wed, Jul 31, 2013 at 3:04 PM, Martin Grigorov <mgrigo...@apache.org>wrote:
> Hi Sebastien, > > > On Wed, Jul 31, 2013 at 2:37 PM, Sebastien <seb...@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="{"f":"form1","u":"./autocomplete?1-1.IBehaviorListener.1-form-ac","e":"change","m":"POST"}"/> 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.vaynb...@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 <mgrigo...@apache.org >> > >wrote: >> > >> > > On Fri, Jul 12, 2013 at 5:21 PM, Igor Vaynberg < >> igor.vaynb...@gmail.com >> > > >wrote: >> > > >> > > > On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov < >> mgrigo...@apache.org >> > > >> > > > wrote: >> > > > > On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg < >> > > igor.vaynb...@gmail.com >> > > > >wrote: >> > > > > >> > > > >> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov < >> > > mgrigo...@apache.org> >> > > > >> wrote: >> > > > >> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <s...@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 < >> nbpr...@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 < >> > > > >> mgrigo...@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! >> > > > >> >>>>> >> > > > >> >>>>> >> > > > >> >> >> > > > >> >> > > > >> > > >> > >> > >