Last active
February 27, 2018 14:28
-
-
Save james-jlo-long/9ca823a35a49d553b4c830b8cfbdd33f to your computer and use it in GitHub Desktop.
Thinking-aloud a simple WAI-ARIA library that doesn't rely on other frameworks so can work with any of them
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Interface | |
ARIA.normalise = function (attribute) {}; // memoise | |
ARIA.getById = document.getElementById.bind(document); | |
ARIA.identify = function (element, prefix = "", getById = ARIA.getById) {}; | |
ARIA.set(element, { | |
hidden: true, | |
controls: document.querySelector(".element"), | |
labelledby: document.querySelectorAll(".reference"), // can have multiple references | |
label: "abc" | |
}); | |
ARIA.set(element, "checked", "mixed"); | |
ARIA.get(element, "controls"); // -> "id-123" | |
ARIA.getRef(element, "controls"); // -> <div id="id-123"> | |
ARIA.has(element, "attr"); | |
ARIA.hasRef(element, "attr"); | |
ARIA.remove(element, "attr1", "attr2"); | |
ARIA.setRole(element, "role1 role2"); | |
ARIA.addRole(element, "role1", "role2"); | |
ARIA.removeRole(element); ARIA.removeRole(element, "role1", "role2"); | |
ARIA.hasRole(element); ARIA.hasRole(element, "role1"); | |
// Basic implementation (WIP) | |
ARIA.set = function set(element, attribute, value) { | |
if (attribute && typeof attribute === "object") { | |
Object | |
.entries(attribute) | |
.forEach(([attr, val]) => set(element, attr, val)); | |
} else { | |
// TODO: JSLint-friendly check? | |
if (value instanceof Node) { | |
value = ARIA.identify(value); | |
} | |
element.setAttribute(ARIA.normalise(attribute), values); | |
} | |
}; | |
ARIA.get = (element, attribute) => element.getAttribute(ARIA.normalise(attribute)); | |
ARIA.getRef = (element, attribute) => ARIA.getById(ARIA.get(element, attribute)); | |
ARIA.getState = (element, attribute) => { | |
let value = ARIA.get(element, attribute); | |
return (/^mixed$/i).test(value) | |
? "mixed" | |
: (/^true$/i).test(value); | |
}; | |
ARIA.has = (element, attribute) => element.hasAttribute(ARIA.normalise(attribute)); | |
ARIA.hasRef = (element, attribute) => ARIA.getRef(element, attribute) !== null; | |
ARIA.remove = (element, ...attributes) => attributes.forEach((attr) => element.removeAttribute(ARIA.normalise(attr))); | |
// Manipulating the role attribute. | |
ARIA.getRole = (element) => element.getAttribute("role"); | |
ARIA.getRoles = (element) => { | |
let role = ARIA.getRole(element); | |
return role | |
? role.trim().split(/\s+/) | |
: []; | |
}; | |
ARIA.setRole = (element, role) => { | |
role | |
? element.setAttribute("role", role) | |
: element.removeAttribute("role"); | |
}; | |
ARIA.addRole = (element, ...roles) => { | |
let set = new Set(ARIA.getRoles(element)); | |
roles.forEach(set.add, set); | |
ARIA.setRole(element, Array.from(set).join(" ")); | |
}; | |
ARIA.removeRole = (element, ...roles) => { | |
if (roles.length) { | |
let set = new Set(ARIA.getRoles(element)); | |
roles.forEach(set.delete, set); | |
ARIA.setRole(element, Array.from(set).join(" ")); | |
} else { | |
ARIA.setRole(element, ""); | |
} | |
}; | |
ARIA.hasRole = (element, role) => ( | |
role | |
? ARIA.getRoles(element).includes(role) | |
: element.hasAttribute("role") | |
); | |
// ARIA.normalise should have the alias ARIA.normalize | |
var normalise = function (attribute) { | |
var parts = String(attribute) | |
.trim() | |
.toLowerCase() | |
.match(/^(aria\-)?([\w\-]+)$/); | |
var suffix = (parts && parts[2]) || ""; | |
return `aria-${suffix}`; | |
}; | |
var normaliseInterface = { | |
configurable: false, | |
enumerable: true, | |
get: function () { | |
return normalise; | |
}, | |
set: function (newNormal) { | |
normalise = newNormal; | |
} | |
}; | |
Object.defineProperties(ARIA, { | |
normalise: normaliseInterface, | |
normalize: normaliseInterface | |
}); | |
// Might be necessary for pseudo-DOMs | |
ARIA.setDOMAttribute = (element, attribute, value) => element.setAttribute(attribute, value); | |
ARIA.getDOMAttribute = (element, attribute) => element.getAttribute(attribute); | |
ARIA.hasDOMAttribute = (element, attribute) => element.hasAttribute(attribute); | |
ARIA.removeDOMAttribute = (element, attribute) => element.removeAttribute(attribute); | |
// Abstract out the Set stuff because it's useful for references as well. | |
ARIA.List = class extends Set { | |
constructor(value) { | |
let iterable = []; | |
if (typeof value === "string") { | |
value = value.trim(); | |
iterable = value | |
? value.split(/\s+/) | |
: []; | |
} else if (isArrayLike(value)) { | |
iterable = value; | |
} else if (value) { | |
iterable = [value]; | |
} | |
super(iterable); | |
} | |
add(...values) { | |
values.forEach(super.add, this); | |
} | |
delete(...values) { | |
values.forEach(super.delete, this); | |
} | |
toArray(handler, context) { | |
return Array.from(this, handler, context); | |
} | |
toString() { | |
return this.toArray().join(" "); | |
} | |
}; | |
// Maybe ARIA should be chainable? | |
ARIA.chain = function (element) { | |
return new Proxy({}, { | |
get: function (target, name) { | |
return function (...args) { | |
ARIA[name](element, ...args); | |
return this; | |
}; | |
} | |
}); | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Now re-made into an exciting GitHub repo (yay!)