Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF custom component losing input focus on ajax update

I'm writing an autocomplete custom component as a learning exercise with JSF 2.1.3. The idea (which is probably pretty familiar) is to enter some text into and input component and present a list box with matching values. The idea is to have a keyup javascript event on the input which calls jsf.ajax.request() to update the component. So far I've got a component which I can include like this:

<mycc:autocomplete id="myauto" searchMethod="#{bean.doSearch}"/>

This renders html like this:

<span id="myauto">
  <input type="text" id="myauto_input" name="myauto_input"
    onkeyup="com.myco.ajaxRequest(this, event)"/>
  <select id="myauto_listbox" name="myauto_listbox">
    <option value="1st">First</option>
    <option value="2nd">Second</option>
  </select>
</span>

The com.myco.ajaxRequest() javascript function (keyup) does this:

jsf.ajax.request(comp, null, {
                 execute: 'myauto',
                 render: 'myauto'
                 });

So because I want to rebuild and rerender the listbox with the suggestions list, I'm re-rendering the custom component 'myauto'. By specifying execute: 'myauto' the decode() method executes and I can get the input value. By specifying render: 'myauto' the encode...() methods execute to regenerate the html.

This is all fine but because I'm rendering the parent of the myauto_input component I lose input focus every time the keyup event fires.

If I specify something like render: 'myauto_listbox' (I only really want to rerender the listbox after all) the problem is that the encode...() methods don't execute, because they're for the custom component as a whole, not just the listbox. And it would be in one of the encode...() methods that I rebuild the listbox containing the suggestions.

The component extends UIInput and I generate markup in a separate renderer (componentFamily = "javax.faces.Input") in the encodeEnd() method (so this always runs after any supplied converter - not yet implemented). I suppose that forcing focus from javascript is a horrible hack and to be avoided.

I'm a bit unsure where to go with this, but I suspect that what I'm seeing indicates that I'm approaching this in the wrong way somehow. If anyone would be good enough to point me in the right direction I'd greatly appreciate it.

like image 898
Oversteer Avatar asked Aug 22 '11 20:08

Oversteer


1 Answers

I've spent some time looking into this and the general issue of losing focus after an ajax update is fairly common and is described in Jim Driscoll's blog (see 'Keeping Focus').

In the case of my custom component I (think I...) have to update the custom component itself which is the parent of the input, so I'm going to lose focus as a result of the ajax update, and that's just the way it is. As such I've looked at what needs to be done to restore focus, and it seems that in my renderer encode I simply have to forcibly restore focus to the input, but only when responding to the POST sent from the onkeyup event by jsf.ajax.request. I use jQuery and just calling .focus() isn't enough because you also have to get the cursor position to the end of any existing input. This code below seems to work ok:

<script>
  jQuery(function($){var cid='#myauto_input';$(cid).focus().focus().click();$(cid).val($(cid).val());});
</script>

(note: .focus().focus().click() required for IE8, just .focus() works on chrome...)

So it looks like the horrible hack has saved the day. I did wonder if there would be any difference if I used the jQuery ajax routines rather than the jsf ajax library but I don't suppose it would make any difference.

like image 80
Oversteer Avatar answered Oct 20 '22 05:10

Oversteer