Skip to content

Instantly share code, notes, and snippets.

@james-jlo-long
Last active February 27, 2018 14:28
Show Gist options
  • Save james-jlo-long/9ca823a35a49d553b4c830b8cfbdd33f to your computer and use it in GitHub Desktop.
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
// 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;
};
}
});
};
@james-jlo-long
Copy link
Author

Now re-made into an exciting GitHub repo (yay!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment