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, API for just that. Update April 2017: Ziptastic has paid plans now. From their FAQ, they say they still have a free version of the Version 2 API which is limited to 100 requests every 24 hours. Our demo will use that!
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 needed 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
See the Pen Using Ziptastic by Chris Coyier (@chriscoyier) on CodePen.
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.
Totally cool. Caution… Causes a complete nerd-gasim.
Thanks for the demo!
Looks like you might want to watch for the tab key in they keyup event handler. Tabbing to the zip field will validate it and auto focus the street address field if there is already a valid zip code.
Very nice.
Usually those with tricky zip codes are typically aware of it already. For instance, my sister lives in Maplewood, MN and has the zip code of 55109. She borders St. Paul, MN and almost every time you put 55109 in (forms, maps, etc) it returns St. Paul, MN.
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.
One small improvement I would make is to move $(“.zip-error”).hide(); to just before the ajax call.
Without this change, it could get confusing to the user if they enter an incorrect zip, and then try to correct it. Even after correction, the error message is still visible until the ajax callback is received.
With the change, the error message disappears after entering a 5-digit integer and while waiting for the callback.
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.
Hi Chris,
Once again, you deliver great stuff. I like the idea of smart forms.
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.
Always provide a radix when using parseInt. parseFloat interprets 05641 as 5641 while parseInt (with no radix) parses that as 13011 (octal 05641). parseInt( ‘05641’, 10) will give you what you want.
Even more unexpected, parseInt (‘05922’) will return simply 5, because 9 is not a valid octal character and parseInt parses until it hits a non-numeric.
Anyone know a way to calculate ‘locations within x miles”?
(cool post btw)!
Bit of a loaded question. You’d need a data set of locations and their center coordinates (or centroids).
You then take the centroid of the location you’re interested in (let’s say San Francisco, CA). You can calculate a bounding box (which is just 4 coordinates representing each corner of the box). You’d then run a query against the data set for other centroids that are within your calculated bounding box.
* centroid ~ 3mi = bounding box
* get centroids within bounding box
Working out distances between two points using Google Maps API =)
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 :)
This works in the states, but what about those of us up in Canadia?
Where’s Canadia? LOL
Zippopotomus according to the notesโฆ You should read the notes.
Zippopotomus according to the notes… You should read the notes.
Very cool stuff. Appreciate it!
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ยข.
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’.
Now we just need a version for the UK.
Now we just need the UK included in this.
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#):
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
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.
Canada has letters in the zip/postal codes. Just sayin’
d.
I’d like to comment on this approach as one coming from the address verification industry. I’m a developer at SmartyStreets, where I’ve had a good deal of experience with USPS address quality products.
Thanks for noting that “addresses are hard”, and that ZIP Codes cross state boundaries. I can take that statement further and say that while ZIP Codes do cross State lines (very rarely), they very often cross City and County lines. It is also quite common for a ZIP Code to have alternate names assigned by the USPS which are valid (and often more appropriate than the default) for use when mailing, as well as some alternate names which are NOT appropriate for use when mailing. My perusing of the code reveals that Ziptastic isn’t taking this into account (“SELECT * FROM zipcodes WHERE zipcode=’$zip’ LIMIT 1” isn’t going to cut it).
Another concern I have with a service like Ziptastic is the underlying data source. The Ziptastic database contains just over 80,000 records, which is about right considering the alternate names I mentioned, but it concerns me that there is no reference in the Ziptastic repository that shows where the ZIP Code data was acquired (please correct me if I’ve missed something). If it was purchased from a 3rd-party, is it licensed to be posted in a public repository such that other people have open access to it? How often does he update it? ZIP Codes are added and removed on a monthly basis by the USPS.
In the end, I respect the authors of Ziptastic–they are working towards solving a real challenge on the web in simplifying UX and reducing user error. I’m concerned that this simplistic approach is creating a false sense of security for those who choose to employ it in their applications. The truth is that the only way to be sure of the correct City/State/ZIP Code combination is to verify an address as a whole, using a CASS-Certified address verification service.
Dude… every place I look all I see is you spamming for Smartystreets.
I won’t use your product just due to sheer annoyance at this point.
You’re getting market saturation, but you’re also spamming the hell out of every message thread I’ve found.
Hi,
It’s a great for Zip code. But would you please tell me that it is internationally approved.
Emm, a little confused. As far as I know $.ajax does not work with external URLs. Does it?
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.
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
Good thing to know.
The reason it didn’t worked for me was, that I was behind a company proxy. Now everything is fine.
I think it works cross domain because their server returns the header:
Access-Control-Allow-Origin: *
It should work for IE8 and greater.
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
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?
US is behind Europe by about 5 years. Give us a break, we will get there.
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.
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).
@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.
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
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.
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?
Still only the states (not international)
I’ll add that soon. Check back tomorrow for a working example.
e.
done:
http://geocoder.ca/?postal=75704-2933&geoit=xml&jsonp=1&callback=test&standard=1&showcountry=1&moreinfo=1
Very slick but it fails in IE-9 – sigh
very cool! but unfortunately, I live in Indonesia. How to replace for the Country Code, URL, Range and Count?
thanks for your attention
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!
Hey! Just FYI for anyone that might be using Ziptastic. We have updated our database to support more countries.
Sometimes its workingin IE and sometimes not. Is there anything can be fixed?
but unfortunately, I live in Indonesia. How to replace for the Country Code, URL, Range and Count?
This really is brilliant, I still think it’s pointless to collect the city/state when the user inputs a zipcode though
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.
Mind blowing. This is something what I was looking for.
Thanks to author. :)
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
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.
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
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
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?
Also, it doesn’t work in any version of IE :(
Not sure why… Anyone?
This code is not work for indians Zipcode like 160015,456001,208214,452001…………….
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
(Not the date here… three years later as I write.)
Someone told me “Ziptastic is dead”, but the website seems alive and well. Not sure what’s up there. The demo also fails on this site, looks like the Ajax request fails (404?).
This was recommended as an alternative: https://code.google.com/archive/p/ziplookup/
I’ll put this at the top of the article instead of down here if I get some kind of confirmation about the state of Ziptastic.