views:

221

answers:

4

I have a SELECT element in which I need to auto-select the appropriate option based on the first half of a postcode entered in a text field. British postcodes are of the form AB12 3CD, where the first section consists of 1-2 letters representing the county and a number representing the area within the county. The last 3 characters are irrelevant for this question.

For most of the fields it is based on only the first letter(s), but for some options it is a postcode range. The HTML should explain it best:

<select id="country_field">
  <option value="">Select</option>
  <option value="AB">AB (Aberdeen)</option>
  <option value="AL">AL (St. Albans)</option>
  <option value="B">B (Birmingham)</option>
  <option value="BA">BA (Bath)</option>
  ...
  <option value="DD1">DD 1-7 (Dundee)</option>
  <option value="DD8">DD 8-11 (Dundee)</option>
  ...
</select>

My code below will currently select the correct element when the value is exactly two letters. But I need to expand it to encompass the single-letter codes (Birmingham) and the postcode ranges (Dundee). Note: I can change the option values if there is a solution that warrants special values, e.g. DD1/DD2 instead of DD1/DD8.

In short:

  • B2 --> Birmingham
  • BA3 --> Bath
  • DD5 --> first Dundee [DD1]
  • DD11 --> second Dundee [DD8]

Here's the Javascript I have so far...

window.onload = function()
{
  // postcode INPUT field
  var zipInput = document.getElementById( 'zip_field' );
  // county SELECT field
  var ctySelect = document.getElementById( 'county_field' );

  zipInput.onchange = function()
  {
    var zipValue = zipInput.value;
    var ctyOptions = ctySelect.options;
    for ( i = 0; i < ctyOptions.length; i++ )
    {
      if ( zipValue.substring(0,2) == ctyOptions[i].value )
        ctyOptions[i].selected = true;
    }
  }
}
A: 

Replace:

zipInput.onchange = function()
  {
    var zipValue = zipInput.value;
    var ctyOptions = ctySelect.options;
    for ( i = 0; i < ctyOptions.length; i++ )
    {
      if ( zipValue.substring(0,2) == ctyOptions[i].value )
        ctyOptions[i].selected = true;
    }
  }

With:

zipInput.onchange = function()
  {
    var zipValue = zipInput.value.match(/^[a-z]+/gi);

    var ctyOptions = ctySelect.options;
    for ( i = 0; i < ctyOptions.length; i++ )
    {
      if (zipValue[0] === ctyOptions[i].value )
        ctyOptions[i].selected = true;
    }
  }
  1. First of all, we removeed the variable assign action from the loop. Why waste cycles repeating the same operation?
  2. Number two, we now filter out everything except the letters in the beginning of the input.
  3. This can in turn be expanded to include the number suffixes, etc.
Dmitri Farkov
+1  A: 

You can use a regular expression to pull out the values...

/^([a-z]{1,2})(\d*)\s/i

Then, for a code with a range like DD, perhaps something like this (pseudo-code)...

if(match[1].value == "DD") {   // needs special processing
  range = match[2].value;
  range = range < 8 ? 1 : 8;   // if the number is less than 8, set it to 1, otherwise set it to 8
  listValue = match[1].value + range
} else                         // just use the letters to select the list item 
  listValue = match[1].value;

So, for DD5, this will return DD1 and for DD11 it will return DD8. Something like B2 or BA3 will simply return B and BA, respectively.

You could change the if to a switch if you have multiple other codes with different ranges. Then, just set the list item with that value as the current selection.

Jon Freeland
I think this is very close to perfect. I'll try it out and report back.
DisgruntledGoat
A: 

You can compare the begining of the zipValue with the options values. No need for regular expressions. Just use indexOf.

  zipInput.onchange = function()
  {
    var zipValue = zipInput.value.toUpperCase();
    var ctyOptions = ctySelect.options;
    for ( i = 0; i < ctyOptions.length; i++ )
    {
      if ( zipValue.indexOf(ctyOptions[i].value) == 0 )
        ctyOptions[i].selected = true;
    }
  }
Nadia Alramli
I wonder how I didn't think of that. Most straightforward solution heh.
Dmitri Farkov
I don't see how this handles the situation where an entry of DD5 will select the list item with the value DD1.
Jon Freeland
I thought he have an option for every district within an area. I might be wrong though.
Nadia Alramli
I may have misunderstood, but from the question: # DD5 --> first Dundee [DD1], # DD11 --> second Dundee [DD8].
Jon Freeland
In any case he says "I can change the option values if there is a solution that warrants special values, e.g. DD1/DD2 instead of DD1/DD8."
Nadia Alramli
Yes, but I believe that means there would still be two list items representing a range of possible input values. We'll find out soon enough.
Jon Freeland
He probably means what you think he means. But I'll keep my answer in case it helped someone else with a similar problem.
Nadia Alramli
Jon is right, I have two option elements in the example, one for DD1-DD7 and one for DD8-DD11, so I would need to match one code to DD1, DD2 etc and one code to DD8, DD9, etc.
DisgruntledGoat
A: 

I'm not sure how this would work in javascript but I'd do something like the following:

  • Set up your values to be regular expressions to match what you're looking for

So then, "B", becomes "^B[0-9]" (I'm assuming it must be followed by a number)

BA becomes "^BA[0-9]"

DD1 becomes "^DD([1-7] )"

DD8 becomes "^DD([8-9] |[1][01] )" to match DD8, DD9, DD10, DD11

Then just run the regex against your string (no need to substring it as the ^ makes sure this match occurs at the start of the string) and check if there was a successful match.

patjbs