Autocomplete Address Form Using Google Map API

30 Nov.2017
Image
Autocomplete Address Form

We are going to be building a basic address form and we are going to use the Google Map API and Places JavaScript APIs to autofill the form when the user types any address or postal code fields, which will either perform an exact address autocomplete or postal code autocomplete, respectively. Also were also going to use the HTML5 Geolocation API to get the user's location, and with that information, both set the bounds for our autocomplete searches and automatically fill in as much of the address as possible. We will use the Google Geocoding API to reverse geocode the user's location and get an approximate address.

For implementation this on your site, you should get your own API key from the Google API Console.

Open a demo on codepen.

File
address-autocomplete.html
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDeoGwJDBE4ic-btiuWCfLk37ehqk2LxPq&libraries=places&callback=GoogleMapsDemo.Application.Init"
        async defer></script>
 
<div class="container-fluid">
 
<form class="form-horizontal" role="form">
    <fieldset class="address">
        <legend>Address</legend>
        <div class="form-group">
            <label class="control-label col-sm-2 col-md-3">
                Address
            </label>
            <div class="col-sm-6 col-md-4">
                <input class="form-control places-autocomplete" type="text" id="Street" name="Street" placeholder="" value="" autocomplete="address-line1">
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-sm-2 col-md-3">
                Apt/Suite #
            </label>
            <div class="col-sm-6 col-md-4">
                <input class="form-control" type="text" id="Street2" name="Street2" value="" autocomplete="address-line2">
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-sm-2 col-md-3">
                Postal/Zip Code
            </label>
            <div class="col-sm-2 col-md-2">
                <input class="form-control places-autocomplete" type="text" id="PostalCode" name="PostalCode" placeholder="" value="" autocomplete="postal-code">
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-sm-2 col-md-3">
                City
            </label>
            <div class="col-sm-4 col-md-3">
                <input class="form-control" type="text" id="City" name="City" value="" autocomplete="address-level2">
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-sm-2 col-md-3">
                Country
            </label>
            <div class="col-sm-4 col-md-3">
                <input class="form-control" type="text" id="Country" name="Country" value="" autocomplete="country">
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-sm-2 col-md-3">
                State/Territory
            </label>
            <div class="col-sm-4 col-md-3">
                <input class="form-control" type="text" id="State" name="State" value="" autocomplete="address-level1">
            </div>
        </div>
    </fieldset>
</form>
 
</div>
File
address-autocomplete.js
var GoogleMapsDemo = GoogleMapsDemo || {};
 
GoogleMapsDemo.Utilities = (function () {  
    var _getUserLocation = function (successCallback, failureCallback) {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(function (position) {
                successCallback(position);
            }, function () {
                failureCallback(true);
            });
         } else {
             failureCallback(false);
         }
    };
 
    return {
        GetUserLocation: _getUserLocation
    }
})();
 
GoogleMapsDemo.Application = (function () {  
    var _geocoder;
 
    var _init = function () {
        _geocoder = new google.maps.Geocoder;
 
        GoogleMapsDemo.Utilities.GetUserLocation(function (position) {
            var latLng = {
                lat: position.coords.latitude,
                lng: position.coords.longitude
            };
            _autofillFromUserLocation(latLng);
            _initAutocompletes(latLng);
        }, function (browserHasGeolocation) {
            _initAutocompletes();
        });
    };
 
    var _initAutocompletes = function (latLng) {
        $('.places-autocomplete').each(function () {
            var input = this;
            var isPostalCode = $(input).is('[id$=PostalCode]');
            var autocomplete = new google.maps.places.Autocomplete(input, {
                types: [isPostalCode ? '(regions)' : 'address']
            });
            if (latLng) {
                _setBoundsFromLatLng(autocomplete, latLng);
            }
 
            autocomplete.addListener('place_changed', function () {
                _placeChanged(autocomplete, input);
            });
 
            $(input).on('keydown', function (e) {
                // Prevent form submit when selecting from autocomplete dropdown with enter key
                if (e.keyCode === 13 && $('.pac-container:visible').length > 0) {
                    e.preventDefault();
                }
            });
        });
    }
 
    var _autofillFromUserLocation = function (latLng) {
        _reverseGeocode(latLng, function (result) {
            $('.address').each(function (i, fieldset) {
                _updateAddress({
                    fieldset: fieldset,
                    address_components: result.address_components
                });
            });
        });
    };
 
    var _reverseGeocode = function (latLng, successCallback, failureCallback) {
        _geocoder.geocode({ 'location': latLng }, function(results, status) {
            if (status === 'OK') {
                if (results[1]) {
                    successCallback(results[1]);
                } else {
                    if (failureCallback)
                        failureCallback(status);
                }
            } else {
                if (failureCallback)
                    failureCallback(status);
            }
        });
    }
 
    var _setBoundsFromLatLng = function (autocomplete, latLng) {
        var circle = new google.maps.Circle({
            radius: 40233.6, // 25 mi radius
            center: latLng
        });
        autocomplete.setBounds(circle.getBounds());
    }
 
    var _placeChanged = function (autocomplete, input) {
        var place = autocomplete.getPlace();
        _updateAddress({
            input: input,
            address_components: place.address_components
        });
    }
 
    var _updateAddress = function (args) {
        var $fieldset;
        var isPostalCode = false;
        if (args.input) {
            $fieldset = $(args.input).closest('fieldset');
            isPostalCode = $(args.input).is('[id$=PostalCode]');
            console.log(isPostalCode);
        } else {
            $fieldset = $(args.fieldset);
        }
 
        var $street = $fieldset.find('[id$=Street]');
        var $street2 = $fieldset.find('[id$=Street2]');
        var $postalCode = $fieldset.find('[id$=PostalCode]');
        var $city = $fieldset.find('[id$=City]');
        var $country = $fieldset.find('[id$=Country]');
        var $state = $fieldset.find('[id$=State]');
 
        if (!isPostalCode) {
            $street.val('');
            $street2.val('');
        }
        $postalCode.val('');
        $city.val('');
        $country.val('');
        $state.val('');
 
        var streetNumber = '';
        var route = '';
 
        for (var i = 0; i < args.address_components.length; i++) {
            var component = args.address_components[i];
            var addressType = component.types[0];
 
            switch (addressType) {
                case 'street_number':
                    streetNumber = component.long_name;
                    break;
                case 'route':
                    route = component.short_name;
                    break;
                case 'locality':
                    $city.val(component.long_name);
                    break;
                case 'administrative_area_level_1':
                    $state.val(component.long_name);
                    break;
                case 'postal_code':
                    $postalCode.val(component.long_name);
                    break;
                case 'country':
                    $country.val(component.long_name);
                    break;
            }
        }
 
        if (route) {
            $street.val(streetNumber && route
                        ? streetNumber + ' ' + route
                        : route);
        }
    }
 
    return {
        Init: _init
    }
})();