donderdag 19 april 2012

ADF 11g: Remove the LOV button from the keyboard tab-sequence: Part 2

In a previous post I described a way to remove the LOV button from the keyboard tab-sequence. This solution was based on a combination of a focus-clientListener and some Javascript. As soon as an LOV item gets focus, the Javascript searches the related LOV button and turns the tabIndex of the button to -1. This way, the button will not be in the tab-sequence anymore.
One comment on this solution was that it will not work for LOV items that are displayed as part of an <af:query> component. This is caused by the fact that an <af:query> component renders its items on the fly, so it is not possible to add a clientListener to an item. However, there is another solution for this problem.

Since the previous post, I've been working on functionality  to 100% keyboard enable our ADF application. This functionality is based on a Javascript library called jQuery, This library turns out to be a very powerful tool in combination with the ADF client side.In this post I will describe a way to remove the LOV button from the keyboard tab-sequence, using jQuery. The solution will cover all LOV items, including the ones in e.g. an <af:query>.

In general, what the solution does, is find all LOV buttons in the page (HTML-document), and turn their tabIndex into -1.For this we first need a Javascript to find and change all LOV buttons. And this is where jQuery comes into place:

Javascript
var lovIcons = $('a[id$="lovIconId"]');

This statements finds all HTML-elements of type <a> with an id that ends with 'lovIconId'. This results in a list of all the LOV buttons.The $ sign in the statement is a reference to the functions in the jQuery library.

The next step is to turn the tabIndex for all the items into -1:

Javascript
$(lovIcons).attr('tabIndex', -1);

In this post I will not go into the details of jQuery, but as you can see a few simple statements can do the job.

To make use of jQuery, download the library from this location: http://code.jquery.com/jquery-1.7.js. Make sure the library is available in your project, and include a reference to the library in your page:

    <af:document id="d1">
      <f:facet name="metaContainer">
        <af:group>
          <af:resource type="javascript" source="/jsLibs/jquery-1.7.js"/>
        </af:group>
      </f:facet>
   .....


Now we need a way to trigger this Javascript. ADF pages are dynamic documents that can show or hide LOV items during different requests. This means that it is not enough to run the script during document-load. The easiest way is to run the script during every request. Perhaps there are more fine-grained ways, but for this post I will keep it simple. To trigger the script we make use of a PhaseListener. This PhaseListener adds the script to every response:

PhaseListener
package view.beans;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import oracle.adf.share.logging.ADFLogger;

import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;


public class DemoPhaseListener implements PhaseListener {
    private static final ADFLogger sLog = ADFLogger.createADFLogger(DemoPhaseListener.class);
    private static final long serialVersionUID = 1L;

    /**
     * placeholder method
     */
    @Override
    public void afterPhase(PhaseEvent phaseEvent) {
    }

    /**
     */
    @Override
    public void beforePhase(PhaseEvent phaseEvent) {
        if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE) {

            FacesContext fctx = FacesContext.getCurrentInstance();
            ExtendedRenderKitService erks = Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
            StringBuffer script = new StringBuffer();

            // Append the jQuery script to find all lov-icons and set their tabIndex to -1

            script.append("var lovIcons = $('a[id$=\"lovIconId\"]'); $(lovIcons).attr('tabIndex', -1);");
            erks.addScript(fctx, script.toString());
        }

    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }

}


The last step is to define the PhaseListener in the faces-config.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee">
  <lifecycle>
    <phase-listener>view.beans.DemoPhaseListener</phase-listener>
  </lifecycle>
  <application>
    <default-render-kit-id>oracle.adf.rich</default-render-kit-id>
  </application>
</faces-config>


So now, if you run your application, all LOV-buttons are skipped! Soon I will write some more about he possibilities of jQuery in combination with ADF clientside scripts.

Geen opmerkingen:

Een reactie posten