Many end users are used to pressing enter after inserting one or more search terms in an HTML form. Many, if not most, online search engines support this. Unfortunately, search areas in UIX pages don’t. Having consulted both the Oracle JDeveloper forum and the Oracle JHeadstart forum, no solution for this seems to be available. On some occasions, statements like "this is not possible at all" can be found in thses forums. Well, it IS possible If you want to know how, read on.
The key lies in JavaScript. Whenever a search item is entered in an input field, JavaScript needs to check if the ENTER key is pressed. If so, the search action needs to be perfomed. Unfortunately, Internet Explorer has a different way of publishing key events than Firefox and similar browsers. So, first the browser needs to be detected. Undoubtedly many scripts exist for this, but the one I found very useful can be found at quirksmode.org.
Search areas in "normal" pages
Next, the key events need to be captured and examined. Again our friends at quircksmode.org provide info on how to do that. A comprehensive overview of browser events and how to capture them in JavaScript can be found on this page.
The JavaScript function I eventually wrote is this.
function doSearch(e, buttonName) {
BrowserDetect.init();
if (BrowserDetect.browser=='Explorer') e = event;
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
buttonToClick = getFormElement(buttonName);
buttonToClick.click();
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
if (BrowserDetect.browser=='Explorer') {
e.returnValue = false;
}
return false;
}
return true;
}
In that code, BrowserDetect.init() and BrowserDetect.browser can be found in the Browser Detect JavaScript function at quirksmode.org. The e.preventDefault and e.stopPropagation checks are needed to make sure that Firefox doesn’t pass on the ENTER event to other layers in the browser.
In order to make the function work with our UIX pages, a few modifications are needed. As you can see in the declaration of the function, the button name needs to be passed on to the function. So please now make sure that your search buttons have an id if they do not already have it. Since I am using JHeadstart to generate my pages, I need to make post-generation changes here.
JHeadstart can generate two kinds of search areas: the quickSearch area and the advancedSearch area. Both are contained in elements that generate divs. The divs are shown or hidden depending on whether the Quick Search or Advanced Search button was clicked. Both areas have their own inputs and their own buttons. In order to capture key events in both areas and to make sure the correct button is "clicked" when ENTER is pressed, I added this property to all input elements in the quick search area
onKeyDown="doSearch(event, 'goButton');"
Besides that, I gave the quickSearch button this next id, since the text on the button is "Go"
id="goButton"
Similarly, I added this property to all input fields in the advanced search area
onKeyDown="doSearch(event, 'findButton');
and this id to the advancedSearch button, since the text on the button is "Find"
id="findButton"
That is enough to get the desired functionality!
Search areas in LOVs
Unfortunately, things aren’t so simple for LOV search areas. Apparently, the UIX element that forms most of the LOV page is "listOfValues" and this element generates a very complete LOV page with all kinds of JavaScript in it. So, the trick turned out to be to override some of that JavaScript and make sure it works properly. To complicate things further, such an LOV page contains two modes. these are the simple mode with only one input field, and the advanced mode with one or more input fields. Which mode is selected depends on the button that is pressed but unfortunately the JavaScript libraries shipped with the UIX runtime are obfiscurated well enough to make reading them quite complicated. So, I chose to write some Javascript of my own.
Inspection of the generated HTML a page with an listOfValues element reveals that the input field in the simple search mode has a key listenening JavaScript function registered. The most importand HTML for such an input field is
<input id="M__Id" title="Search Term" class="x4"
onkeypress="return _chain('return _LovInputSOE(event, \'_LOVResFrm\', \'lovFilter\', \'VB_MyCountryCode\');',
'return _submitOnEnter(event, \'_LOVResFrm\');',this,event,true)"
name="searchText" size="25" type="text">
The onkeypress attribute reveals that two JavaScript functions are chained. the first one is called _LovInputSOE, the second _submitOnEnter. The VB_MyCountryCode string refers to what field in the page that the LOV pops up from the selected item in the LOV page is copied. This string is specific to the LOV page and I will address later how to generify that string.
The name of the second function suggests that some event handling is done for the ENTER key, but it looks like it doesn’t work. I first turned my attention to the _submitOnEnter function. This function also is rendered in the HTML of the LOV page and it looks like this
function _submitOnEnter(e,frm){
if(_getKC(e)==13){
submitForm(frm,1);
return false;
}
}
Apparently some function called _getKC is used to detect if the ENTER key is pressed. I didn’t investigate this function but I bet this is the failing function. To override the existing _submitOnEnter function, I placed this JavaScript at the bottom of the page
function _submitOnEnter(e,frm)
{
if(doLOVSearch(e))
{
submitForm('_LOVResFrm',1,{'searchAreaMode':'simple',event:'lovFilter',source:'VB_MyCountryCode'});
return false;
}
}
Here you can see that I have a reference to VB_MyCountryCode again that I will generify later on. The doLOVSearch(e) function is similar to the doSearch(e, formname) function from earlier in this article, but it is slightly different
function doLOVSearch(e) {
BrowserDetect.init();
if (BrowserDetect.browser=='Explorer') e = event;
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
if (BrowserDetect.browser=='Explorer') {
e.returnValue = true;
}
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
return true;
}
return false;
}
The difference is that now no button gets clicked. I am sure the two functions can be combined but I haven’t done that yet. After making all of these modifications I found that pressing ENTER still didn’t do what I wanted it to do! Aarrgghh! After some searching around in the UIX JavaScript libraries, I found out that the _LovInputSOE function also was a hindrance. I wasn’t able to figure out what this function is for, but disabling it all together so far hasn’t produced any negative side effects:
function _LovInputSOE (a0,a1,a2,a3)
{
}< br />
These three functions are enough to make the ENTER key get detected and the search to be performed! Hurray!
Generalizing the LOV search code
Two problems remain. First of all, whenever a user switches the LOV page to advanced search, hitting enter will reset it to simple mode because of the ‘searchAreaMode’:’simple’ part in my _submitOnEnter function. Secondly, there is a reference to a very specific property of the page that brings up the LOV page in the LOV page. To solve these issues, I used the HTML for the button that makes the LOV switch between advanced and simple mode. The HTML for this button is
<button onclick="submitForm('_LOVResFrm',1,
{'searchAreaMode':'advanced',event:'lovSearchMode',source:'VB_MyCountryCode'});
return false;" type="button">Advanced Search</button>
First of all, the search mode that will be used when clicking the button is present in the HTML for the button. Since only two modes exist, knowing which mode will be switched to means knowing in which mode we are now. Secondly, the source property also is present in the HTML. So, grabbing the info in the onclick attribute of the button will tell us everything we need to know. Now, how do we get hold of the correct button? None of the buttons in the HTML code have an id or a name!
Well, there is only one button that has the text ‘Advanced Search’ or ‘Simple Search’. Both strings contain the substring ‘ Search’ so iterating over all input fields and selecting the one that is of type button and contains the innerHTML that has ‘ Search’ as substring will give us the correct button. I am sure this can be more easily done with XPath but I didn’t go trough the trouble of finding out how to implement this for both Internet Explorer and Firefox.
In order to get hold of the future searchAreaMode and of the id of the source element in the calling page, I wrote some JavaScript to get this info out of the onclick string. Here are the two functions I wrote for that:
function getSearchModeProperty() {
var inputs = _LOVResFrm.elements;
for (var i=0;i<inputs.length;i++) {
if (_LOVResFrm.elements[i].type == 'button' && _LOVResFrm.elements[i].innerHTML.search(' Search')>0) {
var searchMode = 'simple';
if (_LOVResFrm.elements[i].innerHTML.split(' ')[0].toLowerCase() == searchMode) {
searchMode = 'advanced';
}
return (searchMode);
}
}
return 'filter';
}
function getSourceProperty() {
var inputs = _LOVResFrm.elements;
for (var i=0;i<inputs.length;i++) {
if (_LOVResFrm.elements[i].type == 'button' && _LOVResFrm.elements[i].innerHTML.search(' Search')>0) {
var onclick = _LOVResFrm.elements[i].getAttribute('onclick').toString();
if (onclick) {
var onclickArray = onclick.split(',');
var source = onclickArray[4].split("'")[1];
return (source);
}
}
}
return '';
}
To make use of these functions, my _submitOnEnter function needs the be modified slightly:
function _submitOnEnter(e,frm)
{
if(doLOVSearch(e))
{
var searchMode = getSearchModeProperty();
var sourceProperty = getSourceProperty();
submitForm('_LOVResFrm',1,{'searchAreaMode':searchMode,event:'lovFilter',source:sourceProperty});
return false;
}
}
An now the code is generic enough to be able to put it into all of the LOV pages!
To do so, I put all of the necessary code into a JavaScript library that is shipped with JHeadstart. If you put the code into your own library, please make sure to include the library AFTER the closing listOfValues tag for else the script will be included before the listOfValues JavaScript and it will not override the _submitOnEnter and the _LovInputSOE functions. To include the library in the LOV code, put this line directly after the closing listOfValues tag
<script source="${requestContextPath}/jheadstart/form.js"/>
Closing remarks
There is one issue that remains. In an LOV page, pressing enter will only work for the first input field. This is the only field in advanced mode that has _submitOnEnter registered. I am still trying to find a solution for that.
Besides that, I understand that UIX now is superseded by ADF Faces. Unfortunately I haven’t been able to try out this JavaScript code in Faces. It may very well not immediately work and some of the code very probably will have to be modified. But I hope that this article will give you hints on where to start and how to do it. If you do it, please let me know. I’ll be very interested.
Hmm yes, I meant generalizing 🙂
Generifying?? (good story, useful article. Thanks!) Lucas