
Editorial illustration.
Originally published 23 June 2023; rewritten and reviewed on 15 October 2026.
A user selects one state, then immediately selects another. The first request finishes last and replaces the city list with the wrong choices. A cascading dropdown needs to handle that ordinary interaction, not only the first selection in a demonstration.
Reset the dependent field immediately
When the parent changes, clear the old child selection, disable the child while loading and present a useful status. Do not leave an apparently valid city attached to a different state.
A jQuery-style outline can protect against stale results:
let requestVersion = 0;
$('#state').on('change', async function () {
const version = ++requestVersion;
const stateId = this.value;
$('#city').empty().prop('disabled', true);
if (!stateId) return;
try {
const rows = await $.getJSON('/cities', { stateId });
if (version !== requestVersion) return;
for (const row of rows) {
$('#city').append(new Option(row.name, row.id));
}
$('#city').prop('disabled', rows.length === 0);
} catch {
if (version === requestVersion) {
// Display an accessible failure message and retry option.
}
}
});
The endpoint and response fields are application-specific. Add an explicit placeholder and error region suited to the form. Creating option text through the DOM avoids treating a returned name as HTML.
Validate the relationship at submission
The browser’s filtered choices are a convenience. The server must still confirm that the submitted city belongs to the selected state and is available to this user. Do not rely on hiding invalid options as an access control.
Test changes, not just selections
Exercise an empty parent, no matching children, a failed request and rapid changes. When editing an existing record, load and restore the saved values in a deliberate sequence.
A dependable dropdown communicates what is available now. It also recognises when an earlier request has become irrelevant and prevents it from rewriting the user’s newer decision.