DougMck
07-13-2005, 05:21 AM
Hi Guys,
Here at work we have some code in a .js file that ties a text field and a select box together so you have a combo box. This works fine normally, but there is a problem when the user goes 'Back' or 'Forward' to a page that has a combo box on it.
The problem is that the first item in the select box is selected instead of the default (selected) item, which is incorrect.
Here is the code from the js file...
//-------------------------------------------------------------------------------------------------
// List of Values (LOV) control
//-------------------------------------------------------------------------------------------------
// The following array contains a list of lovs names defined in the form. Each time a lov is defined,
// Javascript is emitted that will add the lov name to this list. The names are then converted to
// lov objects from this list by lov_init(), This method is used as the lov creation must be delayed
// until the form is loaded, hence lov_init must be defined as an onLoad event handler.
lov_names = new Array();
// Next array has the restrict flags. These determine if the lovs should restrict entry to text field
// so that only items in the list can be selected, i.e. user cannot define new entries.
lov_restrict = new Array();
// The lov objects
lovs = new Array();
// On load initialisation routine. This creates the lov objects. Note that the HTML objects (i.e.
// the edit fields and poplists) already exist. The hard-coded form reference is not ideal, but
// will work for all of all forms at the time of writing... may need to fix this later.
function lov_init()
{
for (i = 0; i < lov_names.length; i++)
{
lov_name = lov_names[i];
lovs[lov_name] = new lov(document.forms[0], lov_name, lov_name + '_text', lov_restrict[i]);
lovs[lov_name].select.selectedIndex = 0; // Make sure the first entry in the list is selected
}
}
// Constructor
function lov(form, select_name, text_name, restrict)
{
this.select = form.elements[select_name]; // Poplist field part of lov
this.text = form.elements[text_name]; // Text field part of lov
this.restrict = restrict ? true : false; // Allow only list entries to be selected flag
this.previous_contents = ''; // Saved previous contents of the text field
this.input_stack = new Array(); // User input stack
// Set up the initial and current selection lists
this.initial_selections = new Array(); // Saved initial selections
this.current_selections = new Array(); // Current selections
for (var i = 0; i < this.select.options.length; i++)
this.current_selections[i] = this.initial_selections[i] = this.select.options[i];
// Set methods
this.lov_update = lov_update;
// Make text field width match poplist width
this.width = this.select.offsetWidth + 'px';
this.text.style.width = this.width;
// Set event handlers. Note we do this here to avoid cluttering the HTML with JavaScript
this.text.onkeyup = lov_text_onKeyUp; // Text key up handler
this.text.onfocus = lov_text_onFocus; // Text focus handler
this.select.onkeyup = lov_select_onKeyUp; // Select keystroke handler
this.select.ondblclick = lov_select_onDblClick; // Mouse double-click handler
// Save references back to the lov, so event handlers can navigate back
this.text.lov = this;
this.select.lov = this;
// If there is a currently selected value in the select element, copy to the text field
if (this.select.selectedIndex >= 0)
this.text.value = this.select.options[this.select.selectedIndex].text;
// Call the update routine. If there is an entry in the text field this will filter the selection
// list. We call it even if the list is empty as there may be an opportunity for auto-completion.
this.lov_update();
}
// Method to update the selection list
function lov_update()
{
// Use the user input to build a regular expression
var contents = this.text.value.replace('^\\s*', ''); // Filter out leading spaces
if (contents == this.previous_contents) return; // Exit now if nothing has really changed
// Because a regular expression is used in the loop that builds the list of new selections, any regex metacharacters must
// be escaped so that they don't cause the Regex to fail.
var character = '';
var escaped_string = '';
// Loop through the string checking each character.
for (var strpos = 0; strpos < contents.length; strpos ++)
{
character = contents.charAt(strpos);
// If is regex metacharacter add it to the string with back slash in front of it, else just add it to the string as it is.
if (character.match(/^(\^|\$|\.|\*|\+|\?|\=|\||\\|\/|\(|\)|\[|\]|\{|\})$/))
escaped_string = escaped_string + "\\" + character;
else
escaped_string = escaped_string + character;
}
var pattern = new RegExp("^" + escaped_string + '(.*)', "i"); // Regular expression to match user string and extract residue
// Build a list of new selections from the existing ones using the new pattern
var new_selections = new Array(); // Our new selections
var auto_completion = null; // Initialise auto-completion string
for (var i = 0; i < this.initial_selections.length; i++)
{
var option = this.initial_selections[i];
var result = pattern.exec(option.text); // Check if option matches our filter
if (!result) continue; // Continue if no match
// Another option for the newly filtered select list has been found
new_selections.push(option); // Include option in the new selection set
// Update the auto completion string. This records text at the front of the new options that they
// have in common. This is used to automatically extend the user's input to save them some typing.
// For example, if the user types 'm' and the three remaining options are 'mount', 'mountain' and
// 'mounted', we can enter the string 'ount' for them.
var residue = result[1]; // Text remaining after the match
if (auto_completion == null) auto_completion = residue; // Initialise auto-completion string if necessary
// Truncate as required to the smaller of the two strings being compared
var length = Math.min(auto_completion.length, residue.length);
auto_completion = auto_completion.substring(0, length);
residue = residue.substring(0, length);
// Now progressively shorten the strings until we find a match - this is the new auto-completion (if any)
while (auto_completion != residue)
{
auto_completion = auto_completion.slice(0, -1);
residue = residue.slice(0, -1);
}
}
if (new_selections.length < 1 && this.restrict) // If no matches and restricted to list contents, do nothing
{
this.text.value = this.previous_contents; // Restore field (and wipe out new key entered by user)
return;
}
// Update the lov with the new selections
this.current_selections = new_selections;
// Copy the current selections to the poplist
this.select.options.length = 0; // Drop the old list completely - IE requires this
for (var i = 0; i < this.current_selections.length; i++)
this.select.options[i] = this.current_selections[i];
// If we can automatically extend the user's selection, do so
if (auto_completion != null && auto_completion.length > 0)
contents += auto_completion;
// Work out the difference between the new contents and the old, and push this on to the input
// stack in case it needs to be undone. Then update the saved contents.
if (contents.length > this.previous_contents.length) // If we added something...
this.input_stack.push(contents.substring(this.previous_contents.length));
this.previous_contents = contents; // Save the new text field contents
this.text.value = contents; // Update text field contents
// Make sure text and option width are the same as they were initially
this.select.style.width = this.width;
this.text.style.width = this.width;
// Make sure the first entry in the list is selected
this.select.selectedIndex = 0;
}
// onKeyUp event handler for the text field
function lov_text_onKeyUp(event)
{
var down_arrow = 40; // Key code for down arrow
var backspace = 8; // Key code for backspace
var backslash = 220; // Key code for backslash
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
if (event.keyCode == backslash) // Ignore backslash characters
{
lov.text.value = lov.previous_contents; // Discard backslash characters
return false;
}
if (event.keyCode == down_arrow) // Down arrow implemented as shift to select element
{
lov.select.focus(); // Shift focus to the select element
lov.select.selectedIndex = 0; // Select the top option
return false;
}
else if (event.keyCode == backspace) // Backspace intercepted so we can use our undo stack
{ // allows us to remove auto-completions in one chunk
if (lov.input_stack.length > 0) // If something to undo...
{
var s = lov.input_stack.pop(); // Remove from the stack
lov.text.value = lov.text.value.slice(0, lov.text.value.length - s.length + 1); // Wipe out typing
}
}
lov.lov_update(); // Update the lov
return true;
}
// onFocus event handler (triggers on text element part of the lov gaining the focus)
function lov_text_onFocus(event)
{
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
lov.select.selectedIndex = 0; // Select the first element in the list
return false;
}
// onKeyUp event handler for the select field (triggers on key entry to the select element part of the lov)
function lov_select_onKeyUp(event)
{
var enter = 13; // Key code for the enter key
var up_arrow = 38; // Key code for the up arrow key
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
if (event.keyCode == enter) // User has selected an item by pushing enter
{ // Copy selection to the text field and move focus back
lov.text.value = lov.select.options[lov.select.selectedIndex].text;
lov.lov_update(); // Update the lov
lov.text.focus();
return false;
}
if (event.keyCode == up_arrow && event.altKey) // User has pushed <alt>up arrow to return to the text field
{ // Copy selection to the text field and move focus back
lov.text.focus();
return false;
}
return true;
}
// onDblClick event handler for the select field
function lov_select_onDblClick(event)
{
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
// A double click is interpreted as a desire to select the item
lov.text.value = lov.select.options[lov.select.selectedIndex].text;
lov.lov_update(); // Update the lov
lov.text.focus();
return false;
}
As the comments at the top of the code state, when a combo box is to be created Jscript is output (by Perl script) on the HTML page that adds the name of the select to the list, and the lov_init() function is called in the onLoad event handler of the page to tie the two together.
Code to create the elements on the HTML page...
<script language="JavaScript">
lov_names.push('from_account_purpose');
lov_restrict.push(1);
</script>
<input type="text" name="from_account_purpose_text" style="border-bottom-width: 0;" onblur="" /><br>
<select name="from_account_purpose" style="" size="1">
<option value="PRR">Adj to Offcourse Proit Retaind</option>
<option value="RPY">Adjustment - Agent</option>
<option value="PRD">Adjustment to Off Profit Due</option>
<option selected="selected" value="TOG">Option GL Account</option>
<option value="ADC">Adjustment-SubAgent Phonebet</option>
</select>
As mentioned at the beginning, the problem seems to be that the select box is selecting the first value in the list, not the selected value BEFORE the lov_int function is called when the user navigates to the page using the 'Back' button on the browser.
So I am wondering if this is actually an Internet Explorer problem? but how could that be when normal select boxes work fine?
Any ideas or that you have on this would be a great help.
Thanks.
Here at work we have some code in a .js file that ties a text field and a select box together so you have a combo box. This works fine normally, but there is a problem when the user goes 'Back' or 'Forward' to a page that has a combo box on it.
The problem is that the first item in the select box is selected instead of the default (selected) item, which is incorrect.
Here is the code from the js file...
//-------------------------------------------------------------------------------------------------
// List of Values (LOV) control
//-------------------------------------------------------------------------------------------------
// The following array contains a list of lovs names defined in the form. Each time a lov is defined,
// Javascript is emitted that will add the lov name to this list. The names are then converted to
// lov objects from this list by lov_init(), This method is used as the lov creation must be delayed
// until the form is loaded, hence lov_init must be defined as an onLoad event handler.
lov_names = new Array();
// Next array has the restrict flags. These determine if the lovs should restrict entry to text field
// so that only items in the list can be selected, i.e. user cannot define new entries.
lov_restrict = new Array();
// The lov objects
lovs = new Array();
// On load initialisation routine. This creates the lov objects. Note that the HTML objects (i.e.
// the edit fields and poplists) already exist. The hard-coded form reference is not ideal, but
// will work for all of all forms at the time of writing... may need to fix this later.
function lov_init()
{
for (i = 0; i < lov_names.length; i++)
{
lov_name = lov_names[i];
lovs[lov_name] = new lov(document.forms[0], lov_name, lov_name + '_text', lov_restrict[i]);
lovs[lov_name].select.selectedIndex = 0; // Make sure the first entry in the list is selected
}
}
// Constructor
function lov(form, select_name, text_name, restrict)
{
this.select = form.elements[select_name]; // Poplist field part of lov
this.text = form.elements[text_name]; // Text field part of lov
this.restrict = restrict ? true : false; // Allow only list entries to be selected flag
this.previous_contents = ''; // Saved previous contents of the text field
this.input_stack = new Array(); // User input stack
// Set up the initial and current selection lists
this.initial_selections = new Array(); // Saved initial selections
this.current_selections = new Array(); // Current selections
for (var i = 0; i < this.select.options.length; i++)
this.current_selections[i] = this.initial_selections[i] = this.select.options[i];
// Set methods
this.lov_update = lov_update;
// Make text field width match poplist width
this.width = this.select.offsetWidth + 'px';
this.text.style.width = this.width;
// Set event handlers. Note we do this here to avoid cluttering the HTML with JavaScript
this.text.onkeyup = lov_text_onKeyUp; // Text key up handler
this.text.onfocus = lov_text_onFocus; // Text focus handler
this.select.onkeyup = lov_select_onKeyUp; // Select keystroke handler
this.select.ondblclick = lov_select_onDblClick; // Mouse double-click handler
// Save references back to the lov, so event handlers can navigate back
this.text.lov = this;
this.select.lov = this;
// If there is a currently selected value in the select element, copy to the text field
if (this.select.selectedIndex >= 0)
this.text.value = this.select.options[this.select.selectedIndex].text;
// Call the update routine. If there is an entry in the text field this will filter the selection
// list. We call it even if the list is empty as there may be an opportunity for auto-completion.
this.lov_update();
}
// Method to update the selection list
function lov_update()
{
// Use the user input to build a regular expression
var contents = this.text.value.replace('^\\s*', ''); // Filter out leading spaces
if (contents == this.previous_contents) return; // Exit now if nothing has really changed
// Because a regular expression is used in the loop that builds the list of new selections, any regex metacharacters must
// be escaped so that they don't cause the Regex to fail.
var character = '';
var escaped_string = '';
// Loop through the string checking each character.
for (var strpos = 0; strpos < contents.length; strpos ++)
{
character = contents.charAt(strpos);
// If is regex metacharacter add it to the string with back slash in front of it, else just add it to the string as it is.
if (character.match(/^(\^|\$|\.|\*|\+|\?|\=|\||\\|\/|\(|\)|\[|\]|\{|\})$/))
escaped_string = escaped_string + "\\" + character;
else
escaped_string = escaped_string + character;
}
var pattern = new RegExp("^" + escaped_string + '(.*)', "i"); // Regular expression to match user string and extract residue
// Build a list of new selections from the existing ones using the new pattern
var new_selections = new Array(); // Our new selections
var auto_completion = null; // Initialise auto-completion string
for (var i = 0; i < this.initial_selections.length; i++)
{
var option = this.initial_selections[i];
var result = pattern.exec(option.text); // Check if option matches our filter
if (!result) continue; // Continue if no match
// Another option for the newly filtered select list has been found
new_selections.push(option); // Include option in the new selection set
// Update the auto completion string. This records text at the front of the new options that they
// have in common. This is used to automatically extend the user's input to save them some typing.
// For example, if the user types 'm' and the three remaining options are 'mount', 'mountain' and
// 'mounted', we can enter the string 'ount' for them.
var residue = result[1]; // Text remaining after the match
if (auto_completion == null) auto_completion = residue; // Initialise auto-completion string if necessary
// Truncate as required to the smaller of the two strings being compared
var length = Math.min(auto_completion.length, residue.length);
auto_completion = auto_completion.substring(0, length);
residue = residue.substring(0, length);
// Now progressively shorten the strings until we find a match - this is the new auto-completion (if any)
while (auto_completion != residue)
{
auto_completion = auto_completion.slice(0, -1);
residue = residue.slice(0, -1);
}
}
if (new_selections.length < 1 && this.restrict) // If no matches and restricted to list contents, do nothing
{
this.text.value = this.previous_contents; // Restore field (and wipe out new key entered by user)
return;
}
// Update the lov with the new selections
this.current_selections = new_selections;
// Copy the current selections to the poplist
this.select.options.length = 0; // Drop the old list completely - IE requires this
for (var i = 0; i < this.current_selections.length; i++)
this.select.options[i] = this.current_selections[i];
// If we can automatically extend the user's selection, do so
if (auto_completion != null && auto_completion.length > 0)
contents += auto_completion;
// Work out the difference between the new contents and the old, and push this on to the input
// stack in case it needs to be undone. Then update the saved contents.
if (contents.length > this.previous_contents.length) // If we added something...
this.input_stack.push(contents.substring(this.previous_contents.length));
this.previous_contents = contents; // Save the new text field contents
this.text.value = contents; // Update text field contents
// Make sure text and option width are the same as they were initially
this.select.style.width = this.width;
this.text.style.width = this.width;
// Make sure the first entry in the list is selected
this.select.selectedIndex = 0;
}
// onKeyUp event handler for the text field
function lov_text_onKeyUp(event)
{
var down_arrow = 40; // Key code for down arrow
var backspace = 8; // Key code for backspace
var backslash = 220; // Key code for backslash
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
if (event.keyCode == backslash) // Ignore backslash characters
{
lov.text.value = lov.previous_contents; // Discard backslash characters
return false;
}
if (event.keyCode == down_arrow) // Down arrow implemented as shift to select element
{
lov.select.focus(); // Shift focus to the select element
lov.select.selectedIndex = 0; // Select the top option
return false;
}
else if (event.keyCode == backspace) // Backspace intercepted so we can use our undo stack
{ // allows us to remove auto-completions in one chunk
if (lov.input_stack.length > 0) // If something to undo...
{
var s = lov.input_stack.pop(); // Remove from the stack
lov.text.value = lov.text.value.slice(0, lov.text.value.length - s.length + 1); // Wipe out typing
}
}
lov.lov_update(); // Update the lov
return true;
}
// onFocus event handler (triggers on text element part of the lov gaining the focus)
function lov_text_onFocus(event)
{
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
lov.select.selectedIndex = 0; // Select the first element in the list
return false;
}
// onKeyUp event handler for the select field (triggers on key entry to the select element part of the lov)
function lov_select_onKeyUp(event)
{
var enter = 13; // Key code for the enter key
var up_arrow = 38; // Key code for the up arrow key
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
if (event.keyCode == enter) // User has selected an item by pushing enter
{ // Copy selection to the text field and move focus back
lov.text.value = lov.select.options[lov.select.selectedIndex].text;
lov.lov_update(); // Update the lov
lov.text.focus();
return false;
}
if (event.keyCode == up_arrow && event.altKey) // User has pushed <alt>up arrow to return to the text field
{ // Copy selection to the text field and move focus back
lov.text.focus();
return false;
}
return true;
}
// onDblClick event handler for the select field
function lov_select_onDblClick(event)
{
if (!event) event = window.event; // Allow for IE and NS
lov = event.srcElement.lov; // Get a reference to the lov object
// A double click is interpreted as a desire to select the item
lov.text.value = lov.select.options[lov.select.selectedIndex].text;
lov.lov_update(); // Update the lov
lov.text.focus();
return false;
}
As the comments at the top of the code state, when a combo box is to be created Jscript is output (by Perl script) on the HTML page that adds the name of the select to the list, and the lov_init() function is called in the onLoad event handler of the page to tie the two together.
Code to create the elements on the HTML page...
<script language="JavaScript">
lov_names.push('from_account_purpose');
lov_restrict.push(1);
</script>
<input type="text" name="from_account_purpose_text" style="border-bottom-width: 0;" onblur="" /><br>
<select name="from_account_purpose" style="" size="1">
<option value="PRR">Adj to Offcourse Proit Retaind</option>
<option value="RPY">Adjustment - Agent</option>
<option value="PRD">Adjustment to Off Profit Due</option>
<option selected="selected" value="TOG">Option GL Account</option>
<option value="ADC">Adjustment-SubAgent Phonebet</option>
</select>
As mentioned at the beginning, the problem seems to be that the select box is selecting the first value in the list, not the selected value BEFORE the lov_int function is called when the user navigates to the page using the 'Back' button on the browser.
So I am wondering if this is actually an Internet Explorer problem? but how could that be when normal select boxes work fine?
Any ideas or that you have on this would be a great help.
Thanks.