My user interaction process is as follows:
Here is my select dropdown:
<label for="city-selector">Choose your favorite city?</label>
<select name="select" size="1" id="city-selector" aria-controls="city-info">
<option value="1">Amsterdam</option>
<option value="2">Buenos Aires</option>
<option value="3">Delhi</option>
<option value="4">Hong Kong</option>
<option value="5">London</option>
<option value="6">Los Angeles</option>
<option value="7">Moscow</option>
<option value="8">Mumbai</option>
<option value="9">New York</option>
<option value="10">Sao Paulo</option>
<option value="11">Tokyo</option>
</select>
Here is the ajax div that gets empties/populated:
<div role="region" id="city-info" aria-live="polite">
<!-- AJAX CONTENT LOADED HERE -->
</div>
Here is the checkbox list that gets placed inside the ajax div:
<fieldset id="building-selector" aria-controls="building-table">
<legend>Select your favorite building:</legend>
<input id="fox-plaza" type="checkbox" name="buildings" value="fox-plaza">
<label for="fox-plaza">Fox Plaza</label><br>
<input id="chrysler-building" type="checkbox" name="buildings" value="chrysler-building">
<label for="chrysler-building">Chrysler Building</label><br>
<input id="empire-state-building" type="checkbox" name="buildings" value="empire-state-building">
<label for="empire-state-building">Empire State Building</label><br>
</fieldset>
And finally the table that holds the cities that he user adds/removes
<table id="building-table" aria-live="polite">
<caption>List of buildings you have selected</caption>
<tr>
<th scope="col">Building name</th>
<th scope="col">Delete Building</th>
</tr>
<tr>
<td>Empire State Building</td>
<td><button>Delete</button> /td>
</tr>
</table>
I thought I was on the right path by using aria-controls="" and aria-live="", but that doesn't seem to be enough for the screen reader to detect the changes. In fact, I don't know if I'm missing something in my markup, or if I need to trigger any alert events or anything like that, to make this work.
Please consider to use vue.js (light framework) or angular2 (heavy framework) to make this task. It will very simplify that kind of dom-js interaction that you need. Is use it with aria-live="polite" aria-atomic='true' and it works. Here is solution for your case in vue.js:
https://jsfiddle.net/nuLjb4uc/8/
HTML:
In head section put
<script type="text/javascript" src="https://vuejs.org/js/vue.min.js"></script>
And in body
<script type="x-template" id="my-template">
<label for="city-selector">Choose your favorite city?</label>
<select name="select" size="1" id="city-selector" aria-controls="city-info" @change="onCityChange($event)">
<option disabled selected value>-- choose city --</option>
<option v-for=" (index, city) of cities" value="{{index+1}}">{{ city }}</option>
</select>
<fieldset v-if="currentCityBuildings" id="building-selector" aria-controls="building-table">
<legend>Select your favorite building:</legend>
<div v-for="building of currentCityBuildings" aria-live="polite" aria-atomic='true'>
<input v-model="building.checked" id="{{ building.name }}" @click='selectBuilding(building)' value="{{ building.name }}" type="checkbox" name="buildings">
<label for="{{ building.name }}">{{ building.name }}</label><br>
</div>
</fieldset>
<table v-if='checkedCityBuildings' id="building-table" aria-live="polite">
<caption>List of buildings you have selected</caption>
<tr>
<th scope="col">Building name</th>
<th scope="col">Delete Building</th>
</tr>
<tr v-for="building of checkedCityBuildings" aria-live="polite" aria-atomic='true'>
<td>{{ building.name }}</td>
<td><button @click='deleteBuilding(building)'>Delete</button></td>
</tr>
</table>
</script>
<div id="app"></div>
JS:
new Vue({
el: '#app',
data: {
cities: ['Amsterdam','Buenos Aires', 'Delhi', 'Hong Kong', 'London', 'Los Angeles', 'Moscow', 'Mumbai', 'New York', 'Sao Paulo', 'Tokyo' ],
currentCityBuildings: null,
checkedCityBuildings: null,
},
template: '#my-template',
methods: {
onCityChange: function(e) {
var index = e.target.value-1;
// instead of below line, you should call proper AJAX function which, when it get data, put them into this.checkedCityBuildings like 'simulateAJAX function do.
setTimeout(this.simulateAJAX(this.cities[index]), 1000);
},
simulateAJAX: function(city) {
this.checkedCityBuildings = null;
this.currentCityBuildings = [];
this.currentCityBuildings.push({name:'fox-plaza-'+city, checked:false});
this.currentCityBuildings.push({name:'chrysler-building-'+city, checked:false});
this.currentCityBuildings.push({name:'empire-state-building-'+city, checked:false});
},
selectBuilding: function(building) {
building.checked = !building.checked;
this.checkedCityBuildings = [];
for(var b of this.currentCityBuildings) {
if(b.checked) this.checkedCityBuildings.push(b);
}
},
deleteBuilding: function(building) {
this.selectBuilding(building)
},
},
})
I tested it on Chrome + ChromeVox plugin.