Programmatic Client Side Activation of JSF Components

The issue is the following: my web application is riddled with buttons and hyperlinks – things the end user can click on in order to start actions: submit a form, start some sort of background AJAX-style data exchange, navigate or interact with packaged (JSF or otherwise) components like trees, shuttles, data grids etc. My challenge here is that I am in need of ways to activate these controls: instead of the user pressing the designated button or hyperlink, I want to pretend the end user did that. I want programmatic control – responding to other events like the user changing and leaving a field or pressing a button of my own devices instead of the one integrated with the component I want to activate. I want to trick the JSF component – or any other component for that matter – into believing the approriate control was activated by the end user and respond accordingly.

My most immediate challenge was to have a button on one side of the screen be pressed and have the Tree component react as if a tree node – implemented as a JSF Command Link – is selected. I have attempted many server side approaches, one of the things JSF supposedly caters for, but the Tree component would not give way. In the end the solution I arrived at was JavaScript based.

....
 

It really is quite simple: the JSF Command Link is rendered in my HTML browser as an <a> element. If I can locate this element in Browser’s DOM, I can dispatch an event to the element. This event is programmatically created but is treated like a user (mouse) initiated event! One of the things this allows me to do is have the users move around my tree using the keyboard rather than the mouse. They like!

What I have done is the following:

  1. create my own Command Button at the position in the page that is to my (user’s) liking
  2. define an onclick event trigger for this Command Button
  3. create a function to be called from the onclick event trigger; this function will locate the element I want to have activated and dispatches an event to it

The command button with its onclick event trigger looks like this:

<af:commandButton immediate="true" text=">" accessKey=">"
                  onclick="nextPressed();">
</af:commandButton>
 

This may seem ADF Faces specific, but it really is not. The only two things that matter is that this specifies an Action Component – a button in this case – that can be clicked on and that when clicked will invoke the nextPressed() function.

I have created a very simple JavaScript library TreeSupport.js with just two functions 

function nextPressed() {
  links = document.getElementsByTagName('a');
  for (var i=0; i<links.length; i++) {
    if (links[i].title.indexOf('nextnode') != -1) {
     doEventDispatch(links[i]);
    }
  }
}

/* Dispatch a click event into the document tree
      *
      * Note: I would have called this function fireEvent, or
      *       dispatchEvent, however, this would have resulted in the
      *       browser-supplied functions (former in IE, latter in DOM-
      *       compliant browsers) being called. Be sure to avoid that.
      */
     function doEventDispatch(elm) {
       var evt = null;
           //elm = null;

       if(document.createEvent) {
         evt = document.createEvent('MouseEvents');
       }
       if(elm && elm.dispatchEvent && evt && evt.initMouseEvent) {
         evt.initMouseEvent(
           'click',
           true,     // Click events bubble
           true,     // and they can be cancelled
           document.defaultView,  // Use the default view
           1,        // Just a single click
           0,        // Don't bother with co-ordinates
           0,
           0,
           0,
           false,    // Don't apply any key modifiers
           false,
           false,
           false,
           0,        // 0 - left, 1 - middle, 2 - right
           null);    // Click events don't have any targets other than
                     // the recipient of the click
         elm.dispatchEvent(evt);
       }
     }  

To include this library in my JSF page, I use the following line:

<afh:script source="/jheadstart/TreeSupport.js"/> 

because JSF (or at least the ADF Faces Tree component I am working at right now) does not give me the control I need over the ID value, I use the title property to identify the element:

<af:commandLink text="#{' '}"
     rendered="#{HrmTreeTree.tree.rowKey==HrmTreeTree.focusRowKey}"
     immediate="true"
     onclick="return alertForChanges()"
     shortDesc="nextnode"
     action="#{HrmTreeTree.action}">
  <af:setActionListener from="#{node}"
                        to="#{HrmTreeTree.selectedNodeNext}"/>
  <af:resetActionListener/>
</af:commandLink>
 

This specifies a Command Link that is included in every node in the Tree component though only rendered for the currently selected node and even then it is quite invisble (text=”). However, it is there and the JavaScript function will be able to find it and activate it as if it were pressed.

Resources

The core of the solution to my issue was found at: http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/27e7c70e51ff8a99/98cea9cdf065a524%2398cea9cdf065a524 – described by Michael Winter. I owe him one.