npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

marwajs-dom

v0.1.1

Published

Fluent, single-word, chainable DOM utilities + fx/store/net add-ons.

Downloads

16

Readme

marwajs-dom

npm version npm downloads TypeScript License: MIT

Fluent, single-word, chainable DOM utilities — plus optional add-ons for animations, storage, and networking.

ESM-only, TypeScript, zero deps, tree-shake friendly.


Install

npm i marwajs-dom

Core API — import dom, { make } from "marwajs-dom"

Selection

dom("#app"); // select by id
dom(".card"); // select by class
dom("button"); // select by tag
dom("ul > li"); // select with CSS selector
dom(null); // empty Dom (no elements)
dom(element); // wrap a DOM element
dom(document); // wrap document
dom([el1, el2]); // wrap element array
dom(NodeList); // wrap NodeList
make("<p>hello</p>"); // create from HTML string
make("div", { id: "x" }); // create tag with attributes

Properties

dom("li").length; // number of elements
dom("li").first; // first Element | Document | Window | undefined
for (const el of dom("li")) {
  /* iterate */
}

Collection helpers

dom("li").at(0); // Dom at index (returns new Dom)
dom("li").at(-1); // last element
dom("li").each((el, i) => console.log(i, el)); // iterate
dom("li").map((el) => el.textContent); // transform → string[]

Query

dom("#app").find("p"); // descendants matching selector
dom("li").up("ul"); // closest ancestor matching selector
dom("#item").near(".sibling"); // siblings matching selector
dom.root(); // returns document
dom.root("article"); // query document for selector

Content

dom("p").text(); // read textContent
dom("p").text("hello"); // set textContent (returns this)
dom("p").html(); // read innerHTML
dom("p").html("<b>hi</b>"); // set innerHTML (script/on* stripped)
dom("input").val(); // read input value
dom("input").val("abc"); // set input value (returns this)

Attributes & Data

dom("a").attr("href"); // read href attribute
dom("a").attr("href", "/page"); // set href attribute
dom("a").attr("href", null); // remove href attribute
dom("a").attr({ title: "tip", rel: "nofollow" }); // set multiple
dom("div").data("userId"); // read data-user-id
dom("div").data("userId", "42"); // set data-user-id
dom("div").data("userId", null); // remove data-user-id attribute
dom("div").data({ id: "1", name: "x" }); // set multiple data attrs

Class

dom("div").class("active"); // add class "active"
dom("div").class("!active"); // remove class "active"
dom("div").class("?active"); // toggle class "active"

Styles

dom("div").css("color"); // read computed style
dom("div").css("fontSize", 14); // set style (numbers → px)
dom("div").css("margin", "8px"); // set style with string
dom("div").css({ padding: 12, color: "red" }); // set multiple
dom("div").css("color", null); // remove style
dom("div").show(); // remove display:none
dom("div").hide(); // set display:none
dom("div").flip(); // toggle hidden attribute
dom("div").flip(true); // show (remove hidden)
dom("div").flip(false); // hide (set display:none)

Tree operations

dom("#parent").add(child); // append child Dom/Element/Node
dom("#parent").pre(child); // prepend child at start
dom("#middle").before("<hr>"); // insert before each element
dom("#middle").after(el); // insert after each element
dom("span").wrap("div"); // wrap each element (single tag only)
dom(".inner").unwrap(); // remove parent of each element
dom("ul").empty(); // clear innerHTML
dom(".card").rm(); // remove each element from DOM
dom("p").clone(); // deep clone each element
dom("p").clone(false); // shallow clone
dom("#old").replace("<span id='new'>New</span>"); // replace elements
dom("#a").swap("<span id='b'>B</span>"); // alias for replace()

Geometry

dom("#box").box(); // getBoundingClientRect() → DOMRect | null
dom("#box").pos(); // { x, y } — current offset position
dom("#box").pos(100, 200); // set left/top (sets position:relative)

Events

dom("button").on("click", handler); // attach event listener
dom("button").on("keydown", handler, { passive: true });
dom("button").off(); // remove all listeners
dom("button").off("click"); // remove all click listeners
dom("button").off("click", handler); // remove specific listener
dom("button").once("click", handler); // fire once then remove
dom("#list").onD("click", "li", (e, el) => {
  /* delegated */
});

Scroll

dom("#scroller").top(); // read scrollTop (falls back to window.scrollY)
dom("#scroller").top(100); // set scrollTop
dom(window).top(0); // scroll window to top
dom("#scroller").left(); // read scrollLeft
dom("#scroller").left(50); // set scrollLeft

Utilities

dom("li").pipe((d) => d.class("active")); // run fn, return this
dom("li").pipe((d) => console.log(d.length));

Add-on: fx — Micro animations

Install: import { enableFx } from "marwajs-dom/fx" — call once before use.

enableFx(); // patches Dom.prototype — call once

dom("#box").fade(300); // fade in/out (auto-detects)
dom("#box").fade(300, true); // fade in
dom("#box").fade(300, false); // fade out

dom("#box").move(50, 100); // translate to x, y (px)
dom("#box").move(50, 100, 500); // with duration (ms)
dom("#box").move(50, 100, 500, ease.out);

dom("#box").scale(1.2); // scale (default: 1)
dom("#box").scale(1.2, 300); // with duration
dom("#box").scale(0); // scale to 0

dom("#box").rotate(45); // rotate degrees
dom("#box").rotate(360, 600, ease.inout);

dom("#box").stop(); // cancel active animation

dom("#box").to({ opacity: 0.5, scale: 1.1 }, 400);
dom("#box").to({ x: 20, y: -10, opacity: 0.8 }, 300);
dom("#box").to({ "--my-var": 50 }, 200); // CSS custom properties

Easing:

ease.linear;
ease.in; // ease-in
ease.out; // ease-out
ease.inout; // ease-in-out
ease.elastic; // exaggerated ease-out with overshoot
ease.bounce; // symmetric bounce with multiple reversals

Add-on: store — local/session storage

Install: import { store } from "marwajs-dom/store"

const s = store(); // localStorage, no namespace
const s = store("local", "app:"); // localStorage with namespace
const s = store("session"); // sessionStorage, no namespace
const s = store("session", "app:"); // sessionStorage with namespace

s.set("name", "Alice"); // store string
s.set("user", { id: 1 }); // store object (auto JSON.stringify)
s.set("count", 42); // store number
s.set("enabled", true); // store boolean

s.get("name"); // "Alice" (T = string by default)
s.get("name", "fallback"); // with fallback
s.get("user"); // { id: 1 }
s.get("missing"); // null
s.get("missing", "default"); // "default"

s.has("name"); // true
s.has("missing"); // false

s.all(); // { name: "Alice", user: { id: 1 }, ... }
s.all(); // typed: s.all<User>()

s.rm("name"); // remove key
s.clear(); // clear all namespaced keys (or all if no ns)

All methods return this for chaining.


Add-on: net — fetch wrapper

Install: import { net } from "marwajs-dom/net"

const api = net("https://api.example.com", { timeout: 8000 })

// HTTP methods
const { data } = await api.get("/users/1").json()
const { data } = await api.post("/users", { name: "Alice" }).json()
const { data } = await api.put("/users/1", { name: "Bob" }).json()
const { data } = await api.patch("/users/1", { name: "Carol" }).json()
await api.del("/users/1")
const { data } = await api.head("/users").json()

// Query string
api.get("/users", { page: 1, limit: 10, active: true })
// → GET /users?page=1&limit=10&active=true
// null/undefined values are omitted

// Response readers
const { data } = await api.get("/users").json<User[]>()
const { data } = await api.get("/raw").text()
const { data } = await api.get("/file").blob()
const { data } = await api.get("/bytes").bytes()

// ok() — throws if non-2xx
await api.get("/posts/1").ok()  // throws on 404

// Interceptors
api.use(({ url, init }) => {           // before fetch — modify req
  (init.headers as any)["Authorization"] = "Bearer token"
})
api.after((res) => {                  // after fetch — inspect res
  if (res.status === 401) redirect("/login")
})
api.trap((err) => {                   // on error — handle/filter
  console.error("net error:", err)
})

// Client utilities
api.header("X-Request-ID", crypto.randomUUID())
api.base("https://other.com")           // change base URL
api.timeout(5000)                       // change timeout (ms)
api.abort()                             // abort all pending requests

Tests

npm test        # run tests (vitest + happy-dom)
npm run test:watch  # watch mode

Build

npm run build   # TypeScript → dist/ (ESM + .d.ts)

MIT © Mohammad Emran