Skip to content
FormaUIPublic

About

DOMQL is a JavaScript library for querying the DOM, allowing you to read information from a document once or observe it as it changes.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

DOMQL

A declarative approach to managing the DOM.

DOMQL is a JavaScript library for querying the DOM, allowing you to read information from a document once or observe it as it changes. Its queries are written in Nexo.

Preview. No breaking redesign is planned, and a version that breaks what the one before it allowed says how to migrate. The runtime, 1.0.0-preview.3, implements DOMQL's specification 1.0.0-preview.1 on Nexo's language 1.0.0-preview.1, definition version 38. DOMQL declares the DOM as types of its own. It is published to npm and NuGet, as installing it sets out, and you can also build and try it locally. See the status overview, and what 1.0 requires.

Features

  • Queries that shape DOM and browser state into plain data
  • Projections, filters and summaries of collections
  • Watches that keep a result current, as snapshots or live state
  • Optional access, fallbacks and conditionals
  • Locals, functions and calculations
  • Strings, interpolation, conversions and casts
  • Shadow trees crossed explicitly
  • Event subscriptions projected into data
  • Built-in executions that focus, scroll, show popovers and dialogs, write what they own and reach the browser's facilities, each answering what it did
  • Typed extension modules: members, observations, event sources and executions
  • Validation before anything is evaluated
  • Definitions as JSON
  • ES modules with TypeScript declarations, from npm or NuGet

Install

From npm, as ES modules that import Nexo from its own package, @formaui-net/nexo, and that a module imports as import { domql } from '@formaui-net/domql':

npm install @formaui-net/domql@preview

From NuGet, as one bundle with Nexo inside it, which a .NET web application serves at _content/domql/domql.js, as the library guide sets out:

dotnet add package formaui-net.DOMQL --prerelease

Until 1.0, npm's latest tag holds a deprecated placeholder, so install @preview, as above.

Your first query

Suppose a panel contains three items:

<div id="panel">
    <div data-key="a1"></div>
    <div data-key="a2" aria-selected="true"></div>
    <div data-key="a3"></div>
</div>

This query reads the item count and each item's key and selection state:

import { domql } from './src/domql.js';

const panel = document.getElementById('panel');

const query = domql.query(`
    panel {
        count = it.children.count,
        items = it.all("[data-key]") {
            key = it["data-key"],
            selected = it.aria["selected"] == "true"
        }
    }
`, { panel });

const snapshot = query.evaluate();

The result is plain data:

{
    "count": 3,
    "items": [
        { "key": "a1", "selected": false },
        { "key": "a2", "selected": true },
        { "key": "a3", "selected": false }
    ]
}

Inside each object expression, it is the current element, and the field names you write become the result's. A snapshot is immutable and holds no live DOM reference, so evaluating again gives a new one and leaves the old one as it was:

// Your app, a UI library or the user changes the page; DOMQL only reads it.
panel.children[0].setAttribute('aria-selected', 'true');

const next = query.evaluate();

console.log(next.items[0].selected);     // true
console.log(snapshot.items[0].selected); // false

The source import above works when serving the repository; the library guide loads the packaged bundle, and goes on to watching, events, the language and extensions.

Why DOMQL

With DOMQL, a component describes the page state it needs and receives it as data. DOMQL reads that state, follows its changes and keeps the result current.

DOMQL comes from Forma, FormaUI's Blazor component library. Forma's components rely on many scripts: reusable behaviors, operations, and objects that support existing components. Knowledge of the page was spread across them, each script tracking its own part at its own level. As they came to interact, they grew hard to reason about and to cache. Each new case took another script, or another change to one. Each answer the C# side needed was another round trip into JavaScript.

The idea that led to DOMQL was one place to ask about the page's state, as one queries a database, in place of scripts that each track it. A component would ask which element has the focus, how large the window is, whether a container can scroll and where its scroll stands.

To answer such a question by hand and keep the answer current, a component has to:

  1. read the state;
  2. observe what changes it;
  3. schedule the next read;
  4. compare the results;
  5. notify whatever uses them;
  6. and dispose of its listeners when it's done.

Each component that does this by hand repeats the work, and does it a little differently.

DOMQL makes that work one shared contract: it supplies the vocabulary, the observation, the scheduling and the delivery of results. Events and executions extend the same contract to what happens on the page, and to changes the caller asks for.

DOMQL is explicit about what it can't follow. Each member declares whether its changes are observable, partly observable or unobserved. A watch of a partly observable member needs its caller's consent, and a watch of an unobserved one is refused, as how a member changes sets out.

Plain JavaScript stays the simpler choice for a small, local operation. A DOM utility library helps with each read or event, but the application still owns observing, scheduling and delivering. DOMQL earns its place when those responsibilities recur across an application.

Why queries are Nexo scripts

The questions Forma's components asked came to combine, filter and shape what they read in more and more ways. A language expressed them more naturally than more JavaScript coordinating each case, and gave them consistent semantics. That language grew inside DOMQL and became Nexo.

Nexo also gives DOMQL control over what a query means, and sight of how it evaluates:

Stage What DOMQL learns
Before evaluation Which declarations a query may use, and whether their observations allow watching it. A member of a value of unknown type is checked against every declaration it could select.
During evaluation Each member the query actually reads, with its receiver and its arguments. A watch observes what its last evaluation read, starting and stopping observations as those reads change, as dependencies sets out.
After evaluation The result as immutable data, with metadata recording where each item came from, where a projection keeps it, which DOMQL uses to compare one result with the next.

A query is text or a definition, which DOMQL interprets without running it as JavaScript. So a query can be stored or generated, and the C# half, which waits for its design, is planned to send queries from .NET.

A JavaScript callback gives none of this by itself. A restricted JavaScript API, or another embedded language, could provide it with integration and enforcement of its own. Nexo provides it as its hosting contract, and DOMQL declares the DOM as a Nexo module like any other.

Documents

Document What it holds
Library guide Install DOMQL, learn the language by example and use the API
DOMQL specification Queries, the DOM's vocabulary, evaluating and watching a query, its results, events, executions, and the API
Nexo specification The language DOMQL is written in, and its runtime, at the release DOMQL builds against
Version history Each version of DOMQL, with the features it added and what it changed
Changelog Each release of the packages and what it changed
Design How DOMQL is built, and the decisions it did not take
Status What is implemented, what is being built, and what remains
Contributing Report a bug, develop a change and run the checks
Security Report a vulnerability privately

Build and try it

Clone the repository. You'll need Node.js with npm, PowerShell and the .NET SDK pinned in nuget/global.json; CONTRIBUTING.md has the details.

./build.ps1           # The build and the checks
./build.ps1 -Browser  # With the real-browser tests
./build.ps1 -Full     # The npm and NuGet packages, in artifacts/, verified as installed

The NuGet package supplies the bundle at _content/domql/domql.js, and the npm package the sources, each with its TypeScript declarations.

License

Apache-2.0.

About

DOMQL is a JavaScript library for querying the DOM, allowing you to read information from a document once or observe it as it changes.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages