Grow your CSS skills. Land your dream job.

Autofill City & State from Zip Code with Ziptastic

Published by Chris Coyier

Most address fields on web forms ask for city, state, and zip code (or city and post code, outside of the US). But as us nerds often lament, city and state are redundant with zip code. Or at least they can be inferred from a correctly entered zip code. That's the kind of thing computers are good at. What we need is a proper API to cough up that information for us on demand.

Enter Ziptastic, a free (donations welcome) API for just that.

Let's use it.

Form Markup

Five fields. Two for street then city, state, and zip. All wrapped up in a form and fieldset. Nothing special.

<form action="#" method="post" class="fancy-form">

  <fieldset>
    <legend>Address</legend>

    <div>
      <div>
        <input type="text" name="address-line-1" id="address-line-1">
        <label for="address-line-1">Street #1</label>
      </div>
    </div>
    <div>
      <div>
        <input type="text" name="address-line-2" id="address-line-2">
        <label for="address-line-2">Street #2</label>
      </div>
    </div>
    <div>
      <div class="city-wrap">
        <input type="text" name="city" id="city">
        <label for="city">City</label>
      </div>
       <div class="state-wrap">
        <input type="text" name="state" id="state">
        <label for="state">State</label>
      </div>
       <div class="zip-wrap">
        <input type="text" pattern="[0-9]*" name="zip" id="zip">
        <label for="zip">Zip</label>
        <p class="zip-error">Not a real zip code.</p>
      </div>
    </div>

    <div>
      <input type="submit" value="Submit">
    </div>

  </fieldset>

</form>

Only show the zip at first

We'll hide all the divs that wrap each row of form elements. We'll use JavaScript, so that in case the user has JavaScript turned off, the form is still usable.

$(".fancy-form div > div").hide();

Then reveal just the zip code.

form .zip-wrap {
  display: block !important;
}

Front end validation

On the front end, we're already doing the best we can to help the proper entry of a zip code through HTML5 input attributes pattern, maxlength, and required.

<input type="text" pattern="[0-9]*" maxlength="5" required name="zip" id="zip">

Notice it's not of type number. Whenever considering type=number, consider "would I be cool with the browser adding commas inside of this number?" and if it's no, don't use it, because some do.

jQuery: Watch for entry of valid zip code

We're going to watch the zip input for keystrokes. Should the final value after a keystroke be a valid zipcode, we'll attempt to get the city and state through Ziptastic and reveal the other fields.

$("#zip").keyup(function() {
  var el = $(this);

  if ((el.val().length == 5) && (is_int(el.val()))) {
   
    // Make Ajax call, etc
 
  }

}

The is_int function is just some extra protection that the number is an integer (like all zip codes) in case the current browser doesn't support the necessary HTML5.

function is_int(value){ 
  if ((parseFloat(value) == parseInt(value)) && !isNaN(value)) {
      return true;
  } else { 
      return false;
  } 
}

jQuery: Ajax the data

Yeah so jQuery. We used it above to make event handling on the input easier, but it's really need here because of its ability to make ajax calls with error handling sixty two times easier than doing it with vanilla JavaScript.

After the passed validation, we can make the Ajax call. All we give it for data is the zip code we've collected and we get some JSON back which is trivially easy to access the city and state and apply them as values to the appropriate inputs.

$.ajax({
  url: "http://zip.elevenbasetwo.com",
  cache: false,
  dataType: "json",
  type: "GET",
  data: "zip=" + el.val(),
  success: function(result, success) {

    $(".fancy-form div > div").slideDown(); /* Show the fields */

    $("#city").val(result.city); /* Fill the data */
    $("#state").val(result.state);

    $(".zip-error").hide(); /* In case they failed once before */

    $("#address-line-1").focus(); /* Put cursor where they need it */

  },
  error: function(result, success) {

    $(".zip-error").show(); /* Ruh row */

  }

});

Of course we can validate all day long for the valid format of zip codes, but not every 5 digit integer is a zip code. Should we ask Ziptastic for a zip code that doesn't exist, it will return an error. In that case we just show an error message.

Demo and Download

View Demo   Download Files

Snag the ZIP. GET IT?! Also, if someone wants to apply some actually nice UX and design to this, I'd be happy to update the demo with credit. Andru Stoicescu at Better <html> submitted a bit of a fancier design so I updated to that.

Notes

  • I'm not going to tell you this is bulletproof. Addresses are hard. I heard some zip codes cross state lines.
  • Ziptastic is US only. Just days after publishing this, Ziptastic starts supporting postal codes internationally. Zippopotamus is similar and supports 60 counties.

Comments

  1. Totally cool. Caution… Causes a complete nerd-gasim.

  2. This is great! I noticed the same thing Katie noticed – I also used a zip code I grew up in outside of Minneapolis but it filled the city in with Mpls.

    but it gets the point across and post offices usually deliver correctly even if you put down the big city that’s nearby.

  3. tomByrer

    Thanks SO MUCH! I always wonder why most websites don’t try to auto-suggest my zip, though most browsers’ auto-fill help me stay lazy ;)

    Good tip Travis.

  4. Hi Chris,

    Once again, you deliver great stuff. I like the idea of smart forms.

  5. LastLifeLost

    Found a problem, should be easy to solve, though: It doesn’t play nice with ZIP codes starting with 0 – for instance, all of Vermont begins with 05 – I’m 05641 (Barre, VT). Entered, doesn’t register. Tried again with 01234 (Boston/Bedford, MA) and also no joy. My guess is the script is ignoring leading 0s.

  6. Chris Wiegman

    Anyone know a way to calculate ‘locations within x miles”?

    (cool post btw)!

    • David

      I find out the latitude / longitude of the entered post code, then do a calculation on the database query comparing it to those stored (If you have them stored), that way you can do like X stores near Y post code, as once you’ve got the distance worked out, you can do a where in the select to whatever distance you want specified, e.g. 50 miles, 100, etc.

      An example of it at work would be: http://www.craftcourses.com/search/PO8%208PT

      Of course – this is worked out in straight distance (not distance via a road), so 100 miles could mean 140 miles on road. If you want further details on how to do it just let me know :)

  7. This works in the states, but what about those of us up in Canadia?

    • Dallan Clawson

      Where’s Canadia? LOL

      Zippopotomus according to the notes… You should read the notes.

  8. Dallan Clawson

    Zippopotomus according to the notes… You should read the notes.

  9. Dallan Clawson

    Very cool stuff. Appreciate it!

  10. Definitely love this demo, but I wonder if this introduces some usability questions? If I come to a web form, expecting to see an address form, but only see a zip code field, I may (mistakenly) believe that the form didn’t load properly or that the page is broken.

    I realize this is a demo, so I’m not trying to call this out as being ‘wrong’. But as a suggestion for an actual application of this pattern, you should look at including some microcopy that says “Hey! Just enter your zip code and we’ll take care of the city and state stuff!”

    Just my 2¢.

    • Strangepants

      Maybe leave the city/state fields visible, but disable and fade them out a little (in addition to your micro-copy).

    • True, you could do that, and that’s probably what I would do: using Javascript, apply a class to the form/form elements in order to have those fields fade out.

      I would just be afraid that by going this route you would need to display the fields in the traditional order: the city and state fields would come before the zip code field. To do otherwise would introduce another potentially jarring usability hiccup. So if you keep the traditional order, you run the risk of users ignoring the microcopy (not the developer’s fault, I realize), and entering the items in order (1. city, 2. state, 3. zip), rending this method useless for this use case. Again, not the end of the world if you go this happens. And the user ignoring both your copy and visual clues is probably a fringe case. I’m just kind of thinking out loud here…

    • Actually, I think I’m overanalyzing this.

      Also, ‘rending’ should be ‘rendering’.

  11. Now we just need a version for the UK.

  12. Now we just need the UK included in this.

  13. Josh Johnson

    Fantastic API. Just for reference: I put together a .NET interface for this service here. Reference it in your ASP.Net, C#.Net, or VB.Net project and use it as follows (C#):

    
    using ZipTastic;
    
    ZipCity info = ZipTastic.ZipInfo.GetZipInfo("16101");
    Label1.Text = info.Country;
    Label2.Text = info.State;
    Label3.Text = info.City;
    
  14. Phil Doughty

    Great idea – I’ve lived in 10 countries but never in USA

    If 1st field:
    *Country*

    2nd field:
    *Zip/Post Code*

    Would make it internationally usable

  15. Chris

    Especially so for place names that contain punctuation etc. having the matching zip code is a matter of clean data, to make sure that there was no typo (say in the zip code) and so that you can mine clean data. It’s also a waste of people’s time entering the same (full address) information all the time in forms. Hence proper automated assistance in this regard is most welcome, and people don’t get frustrated filling it in. Frustration with forms can lead to loss in sales, if you have client facing sign-up or order forms.
    All the forms where you have to repeat a lot of already known information (because you’ve been given an identifier) is a pain we can do without. If you added up all the wasted time filling in duplications of data for all systems in the world, it would be many lifetimes worth of time…essentially stemming from inadequate data collection and processing systems. And many lifetimes are spent cleaning up dirty data that shouldn’t be.
    …quietly stepping off pedestal.

  16. Canada has letters in the zip/postal codes. Just sayin’

    d.

  17. Hi,
    It’s a great for Zip code. But would you please tell me that it is internationally approved.

  18. Emm, a little confused. As far as I know $.ajax does not work with external URLs. Does it?

    • Qbit

      I think you are right. I read about it some days ago and it has something to do with the same origin policy. The jQuery documentation says:

      “Due to browser security restrictions, most ‘Ajax’ requests are subject to the same origin policy; the request can not successfully retrieve data from a different domain, subdomain, or protocol.”

      But this policy is browser dependent and maybe thats the reason why the demo dosn’t work at all in my Firefox (407 Proxy Authentication Required). I think there are some ways to get around this problem, but I’m not sure if they are used here.

  19. CSS loading animations

    There is no workaround for that – I tried a lot of things and finally came out with a server CURL :). Only one thing makes me doubt about my thoughts – JSON. May be AJAX with JSON is allowed on non-origin domains. Chris, could you clarify this, please.

    • You know what. I’m really not sure why this works. I can say it works in all the Firefox browsers I’ve tested. Cross domain restriction is pretty consistent across browsers in my experience.

      Normally, the answer is JSONP which is a simple/clever way around this without involving anything server-side. But that doesn’t look like it’s involved here.

      Someone else know why this works?

    • Looked through some articles – yes, the reason is that you are using JSON. It really IS allowed to use asynchronous cross-domain JS requests. You opened a new thing for me in JQuery/Javascript. It changes a lot of things :) Thanks a lot

    • Qbit

      Good thing to know.

      The reason it didn’t worked for me was, that I was behind a company proxy. Now everything is fine.

    • Brian

      I think it works cross domain because their server returns the header:
      Access-Control-Allow-Origin: *
      It should work for IE8 and greater.

  20. tilakapash

    Can someone explain to me why the following equality check in the is_int function ?
    parseFloat(value) == parseInt(value)

    Is it to check if the string has browser added “,” or “.” ? How does it relate with the isNaN check ? I’m confused and it is picking my brain.

    Thanks, great post

  21. I’m confused – does this not already exist in the US? When I buy goods on the net here in the UK, I usually only have to provide a house number/name and my post code, and the address (street name, city/town, country etc) is auto-filled. It’s been around for probably at least 5 years?

    • @licensetoil

      US is behind Europe by about 5 years. Give us a break, we will get there.

  22. While I agree it isn’t “bulletproof” as you said, I’m not sure it would matter if you were to use it on a small business website so long as you were conscious of where the customer base was located.

    All in all I think this will end up being a very useful tool.

  23. Marc

    Cool tool, sadly the demo doesn’t seem to be working with RI zipcodes, as someone stated earlier it may be the leading zeros (RI zipcodes start this way).

  24. dj

    @chris – as you point out: addresses are hard. I think I have a bit of an answer for this but can’t code it for you. A bit of a sore point for us, we paid a substantial amount for a therapeutic bed which “couldn’t” be delivered because their computer didn’t recognize the zip code that our area had been changed to 11 months prior (no kidding). Recovery attempts escalated to the point of involving TV’s “get Gephardt” (ombudsman) and got us a free bed. Still 5 years later I find some gas-pumps don’t recognize our zip because of (you guess).

    The PO system is free, and liable, to change boundaries and numbers at their discretion so AT BEST this should only be thought of as something to ‘pre-fill’ a DEFAULT field. 1) ask ZIP first (so most people fill it); 2) expand fields – with pre-filled (if possible) fields; 3) IF integer, regex, AJAX fails: provide “we don’t recognize that zip” warning; but, 4) always allow, and never prevent user’s manual entry.

  25. Demo doesn’t work for me with our local zipcodes like 90260, 90278, etc.. Tried FF 12 and IE8

    I tried the demo with 90260 (my ZIPCODE) and immediately upon entering the 5th/last digit, it gives an error statement “Not a real zip code.” Tried a few other known valid zips, same error. Also ignored error, pressed enter key & it clears the field, with no display of city, state (do see empty fields that instantly disappear).

    Thank You

    Craig

  26. http://geocoder.ca has been providing the same functionality for the last 5 years now. eg.

    http://geocoder.ca/?postal=K1R7S8&geoit=xml&jsonp=1&callback=test&standard=1

    for canada
    or
    http://geocoder.ca/?postal=10001&geoit=xml&jsonp=1&callback=test&standard=1 for the usa

    it also reverse geocodes based on lat/lon and more. and it has been and will always be, free.
    if you need more features let me know.

  27. cnwtx

    How about including the ZIP+4 in the US? Like, for instance: 75704-2933. Which could return an address like this:

    123 Somewhere Street
    Nowhere, Texas 75704

    Is this possible?

  28. I’ll add that soon. Check back tomorrow for a working example.

    e.

  29. Rick

    Very slick but it fails in IE-9 – sigh

  30. very cool! but unfortunately, I live in Indonesia. How to replace for the Country Code, URL, Range and Count?
    thanks for your attention

  31. This is fantastic and a strategy I have been implementing for awhile now with a DB I maintain. Love this API!

    I also really like the hidden elements that are revealed after zip selection as I was placing zip above city/state and always felt it was a bit clunky.

    May use the transition on just the city/state as opposed to the entire form.

    As always great post my friend!

  32. Hey! Just FYI for anyone that might be using Ziptastic. We have updated our database to support more countries.

    • Sunil
      Permalink to comment#

      Sometimes its workingin IE and sometimes not. Is there anything can be fixed?

  33. but unfortunately, I live in Indonesia. How to replace for the Country Code, URL, Range and Count?

  34. This really is brilliant, I still think it’s pointless to collect the city/state when the user inputs a zipcode though

  35. As it now supports international postal codes it may be an idea to update the example to allow for lengths greater than 5 as this is needed for international postal codes.

  36. Mind blowing. This is something what I was looking for.
    Thanks to author. :)

  37. Until recently we used Google maps to solve this. Still looking for good alternatives. For some projects we also need geolocation so best choice for now seems to be setting up our own Nominatim install with data from openstreetmap.org

  38. Maron Aquillo

    Hi Chris,
    I’ve been following your articles and learn so much from it. Thanks for being one of my inspiration in creating beautiful websites. Looking forward for your other articles that really helped me a lot.

  39. cibsrk

    This is great work!!!

    Can I pull the source date from My SQL instead?

    Mysql database is local and is called Psyc with a table called Zip:

    I have the following fields in zip table:

    Stree1
    Stree 2
    City
    State
    Zip

    I really need to get data from Mysql, so I really appreciate any help.

    Thanks in advance,

    cibsrk

  40. Hmm, I tried the area code for chicago, 60657, and it put in the city as graceland. This is incorrect.

    Perhaps it is because they switched the area code to having more than five digits? The official area code for the area I am looking for in Chicago is 60657-2012

  41. Anyone know why the City comes in chopped up?

    For example: 19103
    PHILA, PA

    Why isn’t the whole name Philadelphia spelled out? Is there anyway to get that?

  42. Also, it doesn’t work in any version of IE :(
    Not sure why… Anyone?

  43. Feelings
    Permalink to comment#

    This code is not work for indians Zipcode like 160015,456001,208214,452001…………….

  44. laura
    Permalink to comment#

    we need a product that will enter the zip code …i have name, address, town and state…just looking for the zip…does a product like this exist? thank you~laura

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".