@laserware/dominator
    Preparing search index...

    Dominator

    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:

    1. You need to call setAttribute for each attribute you want to set.
    2. You have to stringify each value before setting it.
    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.