1 2 Previous Next 16 Replies Latest reply on Sep 3, 2009 9:13 PM by nbelaevski

    blur + rerender +focus=

      It is a pretty simple thing, you write something in an input box, and a selectOneMenu changes its selection to match what you write in the inputbox... why do I need to do that? (because I am replicating the UI of a legacy Oracle Forms system and it works like that, and the customer likes that, so it has to work like that)

      I worked pretty fine... until the customer decided to start using the keyboard (FFox 3.5/ Richfaces 3.2.2):

      1) Write in the inputbox
      2) Press TAB
      3) rerendered selectOneMenu
      4) The focus is now lost


      Here some of the code:

       <h:inputText id="value" required="true"
       value="#{testSelectRerenderFocus.value}" styleClass="focused">
       <a:support event="onblur" action="#{testSelectRerenderFocus.selectValue}"
       reRender="selectOneNumberList"/>
       </h:inputText>
      
      
       <h:selectOneMenu id="selectOneNumberList" value="#{testSelectRerenderFocus.selectedValue}">
       <s:selectItems value="#{testSelectRerenderFocus.values}"
       var="item" label="#{item['name']}" itemValue="{item['id']}">
       </s:selectItems>
       </h:selectOneMenu>
      



      The testSelectRerenderFocus.selectValue looks like this:
       public void selectValue(){
       log.info("will set selectedValue");
       this.selectedValue=value;
       log.info("selectedValue is now: #{testSelectRerenderFocus.selectedValue}");
       }
      


      So I said, easy to fix just add focus in a:support :


      <a:support event="onblur" action="#{testSelectRerenderFocus.selectValue}"
       reRender="selectOneNumberList" focus="selectOneNumberList"/>
      
      


      And yes that fixed it... until the customer decided to use the mouse again!

      1) Write in the inputbox
      2) Click another form control (not the selectOneNumberList)
      3) The focus is now in the other form control
      3) rerendered selectOneMenu
      4) The focus is selectOneNumberList

      Of course the customer did not like that, he says that if he clicks a form control, then the focus should stay there (and, that, I have to admit, makes sense)...

      Any hints?

        • 1. Re: blur + rerender +focus=
          nbelaevski

          Hello,

          So, the problem is how to set focus after AJAX request only if component had focus before, right?

          • 2. Re: blur + rerender +focus=

            Yes, exactly

            • 3. Re: blur + rerender +focus=

              I guess I could "plug" javascript code (using JQuery selectors) to listen to the focus/blur events of all the form controls and store the id of the last control with focus, and the use the onstop of a4j:status to restore the focus after the ajax call has finished.

              But... shouldn't this be the standard behaviour? (shouldnt richfaces, by default, correctly (and transparently) preserve the currently focus control?

              • 4. Re: blur + rerender +focus=

                And so JRF-7706 is created.

                • 5. Re: blur + rerender +focus=

                  Of course, there is a inconvenience with my current workaround: If I have multiple regions, I need to configure each region (or the controls in each region) to use a a4j:status that restores the focus...

                  Why there are no option to make a4j:status "global" for ALL regions?

                  • 6. Re: blur + rerender +focus=

                    And this gives birth to feature request RF-7711

                    • 7. Re: blur + rerender +focus=

                      One way to avoid the problem of having this "focus preservation" jQuery trick is that if you have multiple regions there is no way to "globally know" if an ajax richfaces call is happening anywhere...
                      Well, I found one way: using the global queue oncomplete... this makes me able to detect whenever an ajaxcall ends and restore the focus (of course if you have multiple queues then this workaround will not work for you)

                      Example code:
                      /layout/template.xhtml (fragment)

                      <!-- header stuff-->
                      <ui:include src="/layout/queueListener.xhtml" />
                      <!-- Other stuff-->
                       <ui:insert name="body"/>
                      <!-- Other stuff-->
                       <ui:include src="/layout/jQueryTemplate.xhtml" />
                      <!-- footer stuff-->
                      


                      /layout/queueListener.xhtml (important fragment)
                      <!-- ui:composition headers-->
                       <script language="javascript" type="text/javascript">
                      
                       var focusedId;
                      
                       function queueComplete() {
                       jQuery(escapeJQueryId('#'+focusedId)).focus();
                       }
                       </script>
                      
                       <a:queue oncomplete="queueComplete();" />
                      <!-- ui:composition closing -->
                      


                      /layout/jQueryTemplate.xhtml
                      <!-- ui:composition headers-->
                       <a:outputPanel ajaxRendered="true">
                      <rich:jQuery selector="input" query="focus(
                      function(){
                       jQuery(this).addClass('focused');
                       focusedId=jQuery(this).attr('id');
                      })"/>
                      <rich:jQuery selector="input" query="blur(
                      function(){
                       jQuery(this).removeClass('focused');
                      
                      })"/>
                      
                      <rich:jQuery selector="select" query="focus(
                      function(){
                      
                       jQuery(this).addClass('focused');
                       focusedId=jQuery(this).attr('id');
                      })"/>
                      <rich:jQuery selector="select" query="blur(
                      function(){
                       jQuery(this).removeClass('focused');
                      
                      })"/>
                      </a:outputPanel>
                      <!-- ui:composition closing -->
                      


                      That code preserves the focus for all input text and select html elements transparently, as long as you have the global queue enabled (and you do not use other queues anywhere)

                      <context-param>
                       <param-name>org.richfaces.queue.global.enabled</param-name>
                       <param-value>true</param-value>
                       </context-param>
                      


                      • 8. Re: blur + rerender +focus=

                        hope this helps anyone with the same problem, and if you see a flaw (or find a better way to do it) please post it here

                        • 9. Re: blur + rerender +focus=

                          Oh,I forgot to post the code for the escapeJQueryId function, here it is:

                          function escapeJQueryId(id) {
                           return id.replace(/:/g,"\\:").replace(/\./g,"\\.");
                          }
                          


                          And please, if you find this useful, vote for the JIRA issues referenced in previous posts in this same thread

                          • 10. Re: blur + rerender +focus=
                            nbelaevski

                            Note that there are textarea/button/select elements. You can use the following selector:

                            input, button, select, textarea
                            . BTW, there is built-in selector: http://docs.jquery.com/Selectors/input.
                            Also it is possible to add global AJAX listener using A4J.AJAX.addListener() function.

                            • 11. Re: blur + rerender +focus=

                               

                              "nbelaevski" wrote:

                              Also it is possible to add global AJAX listener using A4J.AJAX.addListener() function.


                              Just to be sure I understood correctly, this A4J.AJAX.addListener() works always for all a4j ajax calls regardless of region or queue? (In other words works for all queues for all regions, always no matter what?)

                              • 12. Re: blur + rerender +focus=
                                nbelaevski

                                Yes, listeners added by this method are fired for ALL AJAX request that RF processes.

                                • 13. Re: blur + rerender +focus=

                                  that... is... really... GREAT

                                  • 14. Re: blur + rerender +focus=

                                    I was happy too soon, if I use limitToList="true" then the oncomplete of the global queue is NOT called!

                                    I wonder if A4J.AJAX.addListener() works with limit to list enabled... (and i wonder why the queue is being affected by the limitToList.... does this mean that ajaxCalls with limitToList="true" skip the global queue?)

                                    1 2 Previous Next