I'm attempting to carry out an accessibility fix on a table that has been poorly formatted using incorrect ARIA techniques.
To do this, I've written a MutationObserver to watch the page for instances of the table with role="grid" and dynamically change this to role="table", which addresses an accessibility issue when navigating the table with a screen reader.
I've been able to use the MutationObserver on other elements for this app (e.g., to fix incorrect use of aria-labelledby and role="group") and in those cases, I use the same logic and the MutationObserver executes the callback function and successfully changes the ARIA attributes. However, I can't understand why the MutationObserver is not identifying any role="grid" elements here when they appear in the DOM.
Here is my MutationObserver code:
const callback = (mutationList, observer) => {
try {
for (const mutation of mutationList) {
if (mutation.type === "attributes") {
if (mutation.attributeName === 'role') {
if (mutation.target.getAttribute('role') == 'grid') {
/* Replace ARIA role='grid' with role='table' */
mutation.target.setAttribute('role', 'table');
console.log(`The ${mutation.attributeName} attribute was changed`
+ ` from ${mutation.target} to`
+ ` ${mutation.target.getAttribute(mutation.attributeName)}`);
}
}
} else if (mutation.type === 'childList') {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute('role')) {
if (node.getAttribute('role') == 'grid') {
/* Replace ARIA role='grid' with role='table' */
node.setAttribute('role', 'table');
console.log(`The ${mutation.attributeName} attribute was changed`
+ ` from ${mutation.target} to`
+ ` ${mutation.target.getAttribute(mutation.attributeName)}`);
}
}
}
}
}
}
} catch(e) {
console.error(e.message);
}
};
// Select the node that will be observed for mutations
const targetNode = document.body;
// Options for the observer (which mutations to observe)
const config = {
attributes: true,
attributesOldValue: true,
childList: true,
subtree: true};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
I can't directly copy the HTML source from the app because it's a proprietary app, but the structure of the role="grid" object looks like this:
<div
class="_xzy"
role="grid"
aria-rowcount="1"
aria-colcount="11"
id="HospitalityGrid"
aria-multiselectable="true"
aria-label="Hospitality"
tabindex="0"
style="height:
250px; width: 720px;">
...
</div>
Just to anticipate any questions as to why I'm writing a MutationObserver to fix accessibility rather than requesting that the correct ARIA be implemented in the app itself, I don't have access to the source code or developers and so I'm writing this fix as an extension/add-in to make it more accessible to screen reader users.
To summarise, I tried writing a MutationObserver to automatically catch and fix elements with incorrect ARIA attribute role="grid". Currently nothing is happening as the role="grid" elements are not being identified by the MutationObserver callback function.
I figured out what was wrong. While my MutationObserver was checking the added nodes, I wasn't checking the parent node (i.e., mutation.target
). When I added an if statement to check the mutation.target
node as well, my MutationObserver successfully registered the offending div element with role='grid'
and changed it to role='table'
.
// Callback function to execute when mutations are observed
const callback = (mutationList, observer) => {
try {
for (const mutation of mutationList) {
if (mutation.type === 'childList') {
/* Examine parent node first */
if (mutation.target.hasAttribute('role')) {
if (mutation.target.getAttribute('role') === 'grid') {
mutation.target.setAttribute('role', 'table');
console.log(`The role attribute was changed from `
+ `role='grid' to role='table'.`);
}
}
/* Examine child/descendant nodes */
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute('role')) {
if (node.getAttribute('role') === 'grid') {
node.setAttribute('role', 'table');
console.log(`The role attribute was changed from `
+ `role='grid' to role='table'.`);
}
}
}
}
}
}
} catch(e) {
console.error(e.message);
}
};
// Select the node that will be observed for mutations
const targetNode = document.getElementById('mainContainer');
// Options for the observer (which mutations to observe)
const config = {
attributes: true,
attributesOldValue: true,
childList: true,
subtree: true
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);