Better ergonomics for working with the DOM and TypeScript.
See documentation if you want to get right to the API. For a high-level overview of the library, keep reading!
Let's face it, working with the DOM API kind of sucks (especially with TypeScript). This library aims to make it a little less painful.
The following examples compare the use of DOM APIs with this library.
Instead of creating an Element with the DOM APIs:
const div = document.createElement("div");
div.id = "example";
div.ariaLabel = "Hello!";
You can use the createElement
function:
import { createElement } from "@laserware/dominator";
const div = createElement("div", { id: "example", ariaLabel: "Hello!" });
Finding a single element or multiple elements in the DOM isn't that bad.
Assuming you have this HTML:
<div role="grid" aria-colcount="6">
<div role="rowgroup">
<div role="row">
<div role="columnheader" aria-colindex="1">First name</div>
<div role="columnheader" aria-colindex="2">Last name</div>
<div role="columnheader" aria-colindex="5">City</div>
<div role="columnheader" aria-colindex="6">Zip</div>
</div>
</div>
<div role="rowgroup">
<div role="row">
<div role="gridcell" aria-colindex="1">Debra</div>
<div role="gridcell" aria-colindex="2">Burks</div>
<div role="gridcell" aria-colindex="5">New York</div>
<div role="gridcell" aria-colindex="6">14127</div>
</div>
</div>
…
</div>
Here's a couple of ways you could find element(s) using the DOM APIs:
// Single element:
const firstHeaderRow = document.querySelector(`[role="row"] [role="columnheader"]:first-child`);
// Multiple elements in document:
const allGridCells = document.querySelectorAll(`[role="gridcell"]`);
// Finding all children in parent element:
const grid = document.querySelector("grid");
const gridRows = grid.querySelectorAll(`[role="row"]`);
But there are some issues. For one, document.querySelectorAll
returns a NodeList, which are annoying to work with.
You can use findElement
and findAllElements
instead:
import { findElement, findAllElements } from "@laserware/dominator";
const firstHeaderColumn = findElement(
`[role="row"] [role="columnheader"]:first-child`,
);
// Returns the grid cell elements as an array:
const allGridCells = findAllElements<"div">(`[role="gridcell"]`);
// You can use string selectors for the target and parent to get children:
const gridRows = findAllElements(`[role="row"]`, "grid");
Setting attributes with the DOM APIs has some drawbacks:
setAttribute
for each attribute you want to set.const div = document.createElement("div");
div.setAttribute("role", "gridcell");
// Need to convert boolean to a string:
div.setAttribute("aria-disabled", "true");
// And do the same for numbers:
div.setAttribute("aria-colindex", "1");
You can use setAttribute
or setAttributes
instead. Both functions return the element:
import {
createElement,
setAttribute,
setAttributes,
} from "@laserware/dominator";
let div = createElement("div");
// Set one attribute:
div = setAttribute(div, "role", "gridcell");
// Set multiple attributes:
div = setAttributes(div, {
// You can just use a boolean, no need to stringify:
"aria-disabled": true,
// Same goes for numbers:
"aria-colindex": 1,
});
Removing attributes using the DOM APIs requires that you remove each attribute individually.
Assuming you have this HTML:
<div id="example" role="gridcell" aria-disabled="true">Example</div>
Instead of using the element.removeAttribute
API:
const div = document.getElementById("example");
div.removeAttribute("role");
div.removeAttribute("aria-disabled");
You can use removeAttribute
or removeAttributes
. Both functions return the element:
import {
findElement,
removeAttribute,
removeAttributes,
} from "@laserware/dominator";
let div = findElement("#example")!;
div = removeAttribute(div, "role");
div = removeAttributes(div, ["aria-disabled", "aria-colindex"]);
If you want to check for the existence of attributes using the DOM API, you'd use element.hasAttribute
.
If you want to check if a value matches, you're stuck with the annoyances of getAttribute
. So you're back to dealing with strings.
Assuming you have this HTML:
<div id="example" role="gridcell" aria-disabled="true" aria-colindex="1">Example</div>
Here's how you would check for attributes with the DOM APIs:
const div = document.getElementById("example");
const hasRole = div.hasAttribute("role");
const index = 1;
const isCol = div.getAttribute("aria-colindex") === index.toString();
You can use hasAttribute
, hasAllAttributes
, and hasSomeAttributes
instead:
import {
findElement,
hasAttribute,
hasAllAttributes,
hasSomeAttributes,
} from "@laserware/dominator";
const div = findElement("#example");
const hasRole = hasAttribute(div, "role");
// Check if any of the attributes are present:
const someArePresent = hasSomeAttributes(div, ["aria-colindex"]);
// Check if any of the attributes names and values match:
const someMatchValues = hasSomeAttributes(div, { "aria-colindex": 1 });
// Check if all of the attributes match, you can use `null` to
// check for the _existence_ of an attribute only:
const allMatch = hasAllAttributes(div, {
"aria-colindex": 1,
"aria-disabled": null,
});
Building a CSS selector to find something based on attributes requires a lot of manual labor.
Assuming you have this HTML:
<div role="grid" aria-colcount="6">
<div role="rowgroup">
<div role="row">
<div role="columnheader" aria-colindex="1">First name</div>
<div role="columnheader" aria-colindex="2">Last name</div>
<div role="columnheader" aria-colindex="5">City</div>
<div role="columnheader" aria-colindex="6">Zip</div>
</div>
</div>
<div role="rowgroup">
<div role="row">
<div role="gridcell" aria-colindex="1">Debra</div>
<div role="gridcell" aria-colindex="2">Burks</div>
<div role="gridcell" aria-colindex="5">New York</div>
<div role="gridcell" aria-colindex="6">14127</div>
</div>
</div>
…
</div>
You'll need to write the selectors yourself:
const firstHeaderColumn = findElement(`[role="row"] [role="columnheader"]`);
const secondGridCell = findElement(`[role="gridcell"][aria-colindex="2"]`);
You can use selectAttribute
and selectAttributes
instead:
import {
findElement,
selectAttribute,
selectAttributes,
} from "@laserware/dominator";
const firstHeaderSelector = [
selectAttribute("role", "row"),
selectAttribute("role", "columnheader"),
].join(" ");
const firstHeaderColumn = findElement(firstHeaderSelector);
const secondGridCellSelector = selectAttributes({
role: "gridcell",
// Note that we're using a number:
"aria-colindex": 2,
});
const secondGridCell = findElement(secondGridCellSelector);
You can do more than work with attributes. You can also set, get, remove, and select dataset entries, CSS variables, and styles.