A while back I realized that when using the tomahawk popup component (t:popup) in my ADF faces page that I was having a problem: whenever I had a field (like an af:inputText) inside the popped-up region along with something else, I would get some behaviour I did not want: as soon as my cursor left the field (but stayed within the popup region) focus would leave the input field. This made it awkward when I wanted to have a search field in the popup. But I found a simple solution: in the inputText's onblur property, put the following code: try{ this.focus(); } catch(e){}
What this does is keep it there rather than lose focus. I would really like to know what code is making this field lose focus. I did some searching, but it was very slow going for me, and I needed a quick fix.
This site promotes and supports the development and deployment of JSF, Oracle ADF applications, and other web development topics. If you have interesting facts to share about any of these or any related technologies, please feel free to post a comment.
Wednesday, January 13, 2010
Friday, January 8, 2010
JSFBlueprint auto-suggest field enhancements
What follows is an listing of the javascript that goes with the auto-suggest field you can find in JSF Blueprints.
The original brought up a basic list of suggestions which could be mouse selected. I added code to allow you to arrow down and up directly if you did not want to reach for your mouse. Also I added code (per a suggestion by Frank Nimphius, Oracle Corporation), which would reduce the number of ajax calls...very important for many use-cases of this component.
Please let me know if this is helpful.
The original brought up a basic list of suggestions which could be mouse selected. I added code to allow you to arrow down and up directly if you did not want to reach for your mouse. Also I added code (per a suggestion by Frank Nimphius, Oracle Corporation), which would reduce the number of ajax calls...very important for many use-cases of this component.
This code has been tested against IE 7 and 8 and also the latest version of FF.
gReq = "";
var req;
var requests;
var target;
function getElementX(element){
var targetLeft = 0;
while (element) {
if (element.offsetParent) {
targetLeft += element.offsetLeft;
} else if (element.x) {
targetLeft += element.x;
}
element = element.offsetParent;
}
return targetLeft;
}
function getElementY(element){
var targetTop = 0;
while (element) {
if (element.offsetParent) {
targetTop += element.offsetTop;
} else if (element.y) {
targetTop += element.y;
}
element = element.offsetParent;
}
return targetTop;
}
function getWidth(element){
if (element.clientWidth && element.offsetWidth && element.clientWidth < element.offsetWidth) {
return element.clientWidth; /* some mozillas (like 1.4.1) return bogus clientWidth so ensure it's in range */
} else if (element.offsetWidth) {
return element.offsetWidth;
} else if (element.width) {
return element.width;
} else {
return 0;
}
}
function initRequest(url) {
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
// ********** INSERTED CODE ***********************
// The general idea here is that, the end user is only interested in the
// latest request. As of right now (9/20/2009), I have not seen anything
// to indicate otherwise. Assuming this is true, I am hoping that
// we never have a corresponding backlog of request on the database end.
if (typeof(gReq) != 'undefined' && typeof(gReq.readyState) != 'undefined'
&& typeof(req) != 'undefined' && typeof(req.readyState) != 'undefined') {
// alert("gReq.readyState is " + gReq.readyState + ", and req.readyState is " +
// req.readyState);
gReq.abort();
}
gReq = req;
//1/1/2010, mfons, The above was a brilliant idea, but it did nothing I can discern: still
// querying every keystroke. So now I
// will try waiting .5 seconds before registering the keystroke...if
// they are still typing I will keep waiting until I get something
// that has not changed...then query. See doCompletionDelayed()
// function below.
// ************ END OF INSERTED CODE **************
}
/**
* 12/31/09, mfons - originally doCompletion() called the ajax for each keyclick,
* but really we only want to only call the db if the user has not typed anything new
* in a while. Credit for this idea goes to Frank Nimphius, Oracle Corp.,
* who responded to my query on the technet.oracle.com jdeveloper forum.
* However, I figured the coding out myself.
**/
function doCompletionDelayed(targetName, menuName, method, onchoose, ondisplay, oldTargetValue) {
var target = document.getElementById(targetName);
if (target.value == oldTargetValue) {
var menu = document.getElementById(menuName);
menu.style.left = getElementX(target) + "px";
menu.style.top = getElementY(target) + target.offsetHeight + 2 + "px";
var width = getWidth(target);
if (width > 0) {
menu.style.width = width + "px";
}
var url = "faces/ajax-autocomplete?method=" + escape(method) + "&prefix=" + escape(target.value);
initRequest(url);
if (!requests) {
requests = new Object();
}
requests.menu = menu;
requests.onchoose = onchoose;
requests.ondisplay = ondisplay;
requests.targetName = targetName;
req.onreadystatechange = processRequest;
req.open("GET", url, true);
req.send(null);
}
}
function doCompletion(ev, targetName, menuName, method, onchoose, ondisplay) {
var menu = document.getElementById(menuName);
var evt = (typeof(ev.keyCode) == 'undefined') ? window.event : ev; //IE reports window.event not arg
//alert('ev.keyCode is '+ev.keyCode+' and window.event.keyCode is '+ window.event.keyCode);
/*
Try #1: I am trying to allow people to select items with the arrow keys as
well. Not just on a click of the anchor.
*/
/****************ADDED CODE BEGIN***********/
if (typeof(evt) != 'undefined') { // in firefox, focus event may leave evt undefined.
if (evt.keyCode == 38 /*up*/ || evt.keyCode == 40 /*down*/) {
try {
var childAnchors = menu.getElementsByTagName("a");
var targetAnchor = childAnchors[0];
// Look for highlighted item. If found
for (var i = 0; i < childAnchors.length; i++) {
if (childAnchors[i].className == "selectedPopupItem") {
if (evt.keyCode == 38 && i > 0) {
childAnchors[i].className = "popupItem";
targetAnchor = childAnchors[i - 1];
}
else if (evt.keyCode == 40 && i < childAnchors.length - 1) {
childAnchors[i].className = "popupItem";
targetAnchor = childAnchors[i + 1];
}
else {
targetAnchor = childAnchors[i];
}
break;
}
}
targetAnchor.className = "selectedPopupItem";
} catch (e) { alert("error");}
return;
}
else if (evt.keyCode == 13 /*Enter*/){
try{
var childAnchors = menu.getElementsByTagName("a");
for (var i = 0; i < childAnchors.length; i++) {
if (childAnchors[i].className == "selectedPopupItem") {
try{
childAnchors[i].click(); // It appears that firefox has no "click()" defined for anchors??
} catch(e) {
childAnchors[i].onclick();
}
stopCompletion(menuName);
return;
}
}
} catch(e) {}
}
else if (evt.keyCode == 27 /* Escape */ ) {
stopCompletion(menuName);
return;
}
}
/****************more added code...***************/
// 1/1/2010, mfons, added the following call in order to avoid launching an
// ajax-request (i.e., doing a database query) for each keystroke.
// This wait make sure that the target value does not change for some
// time period (500ms at the moment) before actually doing the query.
var target = document.getElementById(targetName);
//alert(target.value);
setTimeout("doCompletionDelayed('" + targetName + "', " +
"'" + menuName + "', '" + method + "', '" + onchoose + "', " +
"'" + ondisplay + "', '" + target.value + "')", 500);
/****************ADDED CODE END***************/
}
function chooseItem(targetName, item) {
if (!requests.onchoose || requests.onchoose == "null") {
var target = document.getElementById(targetName);
target.value = item;
} else {
requests.onchoose(item);
}
}
function stopCompletion(menuName) {
var menu = document.getElementById(menuName);
if (menu != null) {
clearItems(menu);
menu.style.visibility = "hidden";
}
}
/* Stop completion shortly.
This is necessary because I want to stop completion from the blur
(focus loss event) of the completion text field, but that will also
happen, right BEFORE a link click in the completion dialog is processed.
If this is done synchronously, the link is deleted before it is processed
by stop completion. Therefore, I use the delayed variety which schedules
stop completion instead such that the link is processed first.
*/
function stopCompletionDelayed(menuName) {
/* Would like to shorten timeout but this seems to trip up Safari */
setTimeout("stopCompletion('" + menuName + "')", 400);
}
function processRequest() {
if (req.readyState == 4) {
if (req.status == 200) {
parseMessages(requests.menu);
} else if (req.status == 204){
clearItems(requests.menu);
}
}
}
function parseMessages(menu) {
clearItems(menu);
menu.style.visibility = "visible";
//alert(req.responseText);
var lItemsRE = new RegExp("<item>(.*?)</item>", "g");
var lItems = req.responseText.match(lItemsRE);
for (var i = 0; i < lItems.length; i++) {
//alert("item "+ i+ " is " + lItems[i].replace(lItemsRE, "$1"));
appendItem(menu, lItems[i].replace(lItemsRE, "$1"));
}
// var items = req.responseXML.getElementsByTagName("items")[0];
// for (loop = 0; loop < items.childNodes.length; loop++) {
//
// var item = items.childNodes[loop];
//alert('item is '+ item); // why is item null now? xml is coming back OK, it appears.
// appendItem(menu, item.childNodes[0].nodeValue);
// }
}
function clearItems(menu) {
if (menu) {
for (loop = menu.childNodes.length -1; loop >= 0 ; loop--) {
menu.removeChild(menu.childNodes[loop]);
}
}
}
function appendItem(menu, name) {
var item = document.createElement("div");
menu.appendChild(item);
var linkElement = document.createElement("a");
linkElement.className = "popupItem";
linkElement.href = "#";
linkElement.onclick = function() {
chooseItem(requests.targetName, name);
stopCompletion();
return false;
}
var displayName = name;
if (requests.ondisplay && requests.ondisplay != "null") {
displayName = requests.ondisplay(name);
}
linkElement.appendChild(document.createTextNode(displayName));
item.appendChild(linkElement);
}
Please let me know if this is helpful.
Subscribe to:
Posts (Atom)