Marco
Marco

Reputation: 2473

JQuery Autocomplete (City/State) - Need help making script more robust

The following code performs an autocomplete for city and then selects the associated state. It works well if I follow the convention of Id: City and State. I'm running into situations where i have multiple city/state controls on a page with different ids.

Can someone please help me refactor this code to be more generic?

Thanks

    $("#City").autocomplete({
    source: function (request, response) {
        $.ajax({
            url: "http://ws.geonames.org/searchJSON?country=US",
            dataType: "jsonp",
            data: {
                featureClass: "P",
                style: "full",
                maxRows: 12,
                name_startsWith: request.term
            },
            success: function (data) {
                response($.map(data.geonames, function (item) {
                    return {
                        label: item.name + (item.adminName1 ? ", " + item.adminName1 : ""),
                        value: item.name,
                        code: item.adminCode1
                    }
                }));
            }
        });
    },
    minLength: 2,
    select: function (event, ui) {
        $("#State").val(ui.item.code);
    }
});

Upvotes: 1

Views: 2877

Answers (1)

Patrick M
Patrick M

Reputation: 10989

It looks like your problem would be that when one #City is autocompleted, all the #States update to that one selection, eh? This is partly because you've violated an html rule. ID's are supposed to be unique.

So I would recommend the following:

  1. Make your Id's unique, preferably named in a way that matches what they're for (like id="homecity", id="vacationcity" etc.)

  2. Add a class to your cities and states inputs that you're binding to.

  3. Consider making your state inputs readonly if they aren't already. That lets users know that they aren't supposed to type there and prevents UI events and interactions. Hopefully it doesn't disrupt the autocomplete plugin.

  4. Add an attribute to the markup for the cities to indicate which state input they'll be modifying.

Tying it all together, because I can't put code samples in the middle of the list apparently...

Html:

<input id="homecity" class="autocomplete-city" stateid="homestate"></input>
<input id="homestate" class="autocomplete-state" readonly="readonly"></input>

Javascript:

    $(".autocomplete-city").autocomplete({
    source: function (request, response) {
        $.ajax({
            // your normal ajax stuff
        });
    },
    minLength: 2,
    select: function (event, ui) {
        // Updated. 'this' is captured in the closure for the function here
        // and it is set to the dom element for the input. So $() turns it
        // into a jQuery object from which we can get the stateid attr
        $("#"+$(this).attr('stateid')).val(ui.item.code);
    }
});

And viola! Each input is generically tied to the state they will control. No more conflicting ids and no more exploding event handlers.

You could also pull this off with jQuery data, if you don't like poluting your markup with non-standard attributes (by which I mean the stateid="homestate" in the html section).

Edit: This next paragraph explanation wrong.

According to the docs, in the select function, the ui.item is the jQuery element for the input that has been autocompleted, so it should be the correct #homecity and the attr call should return the correct value for #homestate (keeping with the example). But I've not tried it, so you might want to throw in some null and/or undefined checking in there.

Edit: the ui.item param passed to the select function is the item selected from the menu, not the jQuery object for the input element. ui has the following structure:

Object {item: Object}
  item: Object
    code: "AK"
    label: "Jeuno, Alaska"
    value: "Jeuno"

The correct way to get the attr for the state to update is in from this, captured in the closure of the select function as the DOM element for the input. So running the jQuery function on it $(this) gets you the jQuery object from which you can get your attr() or data().

JSFiddle with multiple autocompletes here: http://jsfiddle.net/KBVxK/1/

Upvotes: 1

Related Questions