@frostsw/frostjs

A lightweight JS for basic DOM manipulation and more.
739
Install
npm install @frostsw/frostjs
Latest Version:1.4.1
License:ISC
Last Updated:Sep 27, 2026
Links: npm
Maintainer: frostsw

FrostJS

A lightweight JavaScript library for DOM manipulation, events, forms and XHR.

Current version: 1.4.1

Installation

From a CDN:

<script src="https://cdn.jsdelivr.net/npm/@frostsw/frostjs@1.4.1/public_html/frostjs.min.js"></script>

With a <script> tag, FrostJS is available globally as FrostJS and its alias fr. FrostJS.version holds the loaded version.

npm (React, Vue, Angular, Vite, webpack)

npm install @frostsw/frostjs
import fr from "@frostsw/frostjs";            // or: import {fr, FrostJS} from "@frostsw/frostjs";
const fr = require("@frostsw/frostjs");       // CommonJS

TypeScript types are included (FrostObject, XhrOptions and the rest can be imported from the package). Importing does not create a global; add window.fr = fr yourself if other scripts expect one. It is safe to import during server-side rendering (Next.js, Nuxt, Angular Universal), as long as FrostJS is only called in the browser.

Use FrostJS after the component is on the page, and pass it elements from refs rather than selectors when you can:

// React
import {useEffect, useRef} from "react";
import fr from "@frostsw/frostjs";

function Profile() {
    const form = useRef(null);

    useEffect(() => {
        const el = fr(form.current);
        el.xhr({responseType: "json"}).then(function (response) {
            console.log(response);
        });
        return () => el.off("submit");
    }, []);

    return <form ref={form} action="/api/profile"><input name="name"/></form>;
}
<!-- Vue -->
<script setup>
import {ref, onMounted} from "vue";
import fr from "@frostsw/frostjs";

const box = ref(null);
onMounted(() => fr(box.value).on("click", e => e.toggleClass("active")));
</script>

<template><div ref="box">Toggle me</div></template>
// Angular
import {AfterViewInit, Component, ElementRef, ViewChild} from "@angular/core";
import fr from "@frostsw/frostjs";

@Component({selector: "app-box", template: `<div #box>Toggle me</div>`})
export class BoxComponent implements AfterViewInit {
    @ViewChild("box") box!: ElementRef<HTMLElement>;

    ngAfterViewInit() {
        fr(this.box.nativeElement)!.on("click", e => e.toggleClass("active"));
    }
}

Keep in mind that FrostJS changes the DOM directly: use it for elements the framework does not re-render (or on events and requests), and let the framework own anything it renders from state. The fr.name shortcuts for fr attributes are created on page load; call fr.refresh() after the component renders to use them.

Getting started

Wrap code that touches the page in fr(function () { ... }). It runs once the DOM is ready (immediately if it already is):

fr(function () {
    fr("#btnSave").on("click", function (e) {
        e.text("Saving...");
    });
});

You can have several fr(function () {}) blocks; they run in order.

Selecting elements

fr(selector) accepts a CSS selector, an element, a NodeList/HTMLCollection, an array of elements or another FrostJS object. It returns a FrostJS object wrapping every match, or null when nothing matches.

Two shorthands are added to CSS selectors:

Shorthand Becomes Example
/name [fr='name'] fr("/login")
@name [name='name'] fr("form @email")

fr attributes

Elements with an fr attribute are registered as properties of fr when the page loads:

<button fr="btnOk">OK</button>
<input fr="">          <!-- an empty fr uses the tag name: fr.input -->
fr.btnOk.text("Hello");

Call fr.refresh() after adding elements with fr attributes to register them (fr.refresh(container) limits it to one container). .fr("name") sets the attribute and registers the element in one step.

Element methods

Setters apply to every matched element and return the FrostJS object, so they can be chained. Getters read the first element.

Traversal

Method Description
.get(index) The DOM element at index (default 0). .get("*") returns all of them; .get("latest") re-runs the selector first.
.element(index) Same as .get() without "latest".
.member(index) The element at index wrapped in FrostJS; .member("*") returns an array of FrostJS objects.
.parent(selector, index) Each element's parent, or its closest ancestor matching selector.
.child(selector) All descendants matching selector (all descendants when omitted).
.forEach(callback) Calls callback(member, index) with each element wrapped in FrostJS.
.filter(callback) Keeps the members for which callback returns true.
.not(selector) Removes the members matching selector.
.is(selector) Whether the first element matches selector.
.length() The number of matched elements.

Content and attributes

Method Description
.html(value) Gets or sets innerHTML.
.self(value) Gets or sets outerHTML.
.text(value) Gets or sets textContent. On a <select> it returns the selected option's text.
.val(value) Gets or sets the value. A <select multiple> returns an array.
.getValue() The form value: checked value or true/false for checkboxes and radios, a File or FileList for file inputs, otherwise .val().
.attr(name, value) Gets or sets an attribute. Pass null to remove it.
.hasAttr(name, index) Whether the element at index (default 0) has the attribute.
.data(name, value) Gets or sets data-name.
.prop(name, value) Gets or sets a DOM property. Pass null to delete it.
.id(value), .name(value), .fr(value) Get or set id, name and fr.
.num(source, name) Reads a number from the value, or from "text", "html", "attr", "css", "data" or "prop" (name is the attribute/style/key). Non-numeric characters are ignored.
.formatNum(source, name) Same as .num(), formatted as 1,234.50.
.json(source, name, decode) Parses JSON from the same sources as .num() (the value by default). Returns {} on invalid JSON.

Classes, styles and state

Method Description
.class(add, remove) Adds and/or removes space-separated classes: .class("active", "inactive"), .class("", "hidden"). With no arguments it returns the first element's classList.
.hasClass(name) Whether any element has the class.
.toggleClass(name) Toggles a class.
.css(property, value, important) Gets or sets an inline style. Pass true as the third argument for !important.
.visible(value) Without arguments, whether the element is displayed. true sets display: block, false sets display: none, a string sets that display value.
.enabled(value) Without arguments, whether the element is not disabled. true/false enables or disables it.

Adding and removing elements

Method Description
.append(content) Appends an HTML string, element, element list or FrostJS object. With several targets, each gets a copy.
.prepend(content) Same as .append(), inserted at the start.
.clone(index) A deep copy of the element at index, wrapped in FrostJS.
.remove(selector) Removes the elements, or only their descendants matching selector.
fr.create(tagOrHtml) Creates an element from a tag name ("div"), an HTML string or a copy of an element.
fr("#list").append("<li>New item</li>");
fr("#list").append(fr.create("li").text("Another"));

Events

fr(".btn").on("click", function (e) {
    alert(e.data("id"));
});

fr("#txtName").on("blur", validateName);
fr("#txtName").off("blur", validateName);
fr("#txtName").off("blur");
fr("#btnSave").trigger("click");
  • .on(event, callback, useCapture) adds a listener to every element.
  • .off(event, callback) removes one listener, or every listener for the event when callback is omitted.
  • .trigger(event) fires the event.

The event passed to the callback also has all the FrostJS methods of the element the listener is on, so e.val(), e.data("id") and e.class("active") work directly. The FrostJS .data() takes the place of the browser's own data property on input, message and composition events. To read that property, use addEventListener instead.

Input filters

fr("#txtAge").typing(fr.Typing.NUMBERS);
fr.Typing. Allows
LETTERS Letters
NUMBERS Digits and a decimal point
MATH Digits and math operators
MONEY Digits, a decimal point and a minus sign
TITLECASE Any text, converted to Title Case as you type
ALLCAPS Any text, converted to UPPERCASE as you type

Forms

.serialise() collects every named field inside the element:

fr("/formProfile").serialise();                // "name=Jo&email=jo%40mail.com"
fr("/formProfile").serialise(true);            // {name: "Jo", email: "jo@mail.com"}
fr("/formProfile").serialise(new FormData());  // FormData, including files

.xhr(options) makes the form submit through fr.xhr() instead of reloading the page, every time it is submitted. It returns a chain with .then() and .catch():

fr("/formProfile")
    .configure({headers: {"api-key": apiToken}})   // optional
    .xhr({asObject: new FormData(), responseType: "json"})
    .then(function (response, form) {
        form.class("saved");
    })
    .catch(function (response, form) {
        alert("Could not save");
    });
  • The request uses the form's action and method (POST by default) and all its named fields. options takes everything fr.xhr() takes, plus asObject to choose the format (new FormData() for file uploads, true for an object). data is added to the form's fields.
  • Options are merged in this order, later ones winning: fr.xhr.configure(), then the form's .configure(), then the .xhr() options.
  • .configure(options) is kept on the form elements, so it also applies to later fr("/formProfile").xhr() calls. .configure() returns the form's options, and .configure(null) clears them.
  • .then() and .catch() handlers run for every submit, not once, and receive the response and the form. .catch() runs on network errors and aborts; HTTP error statuses such as 404 or 500 still go to .then(), as with fr.xhr().
  • The chain also has .configure(options) to add options, and .submit() to submit the form right away.
  • Calling .xhr() again on the same form replaces its previous setup instead of adding a second handler.
  • success and error options still work.

XHR

fr.xhr(options) sends a request and returns a Promise:

fr.xhr({
    url: "api/save.php",
    method: "POST",
    data: {name: "Jo", tags: ["a", "b"]},
    responseType: "json"
}).then(function (response) {
    console.log(response);
});
Option Description
url Required.
method "POST" by default. For GET, the data is added to the query string.
data An object (nested objects and arrays are supported), a query string or FormData.
headers An object of request headers.
async, user, pass, mime Passed to open() and overrideMimeType().
progress Upload progress callback.
success, error, abort Callbacks, in addition to the Promise (which rejects on error and abort).
config false to skip the presets below.
any other Set on the XMLHttpRequest itself, e.g. responseType, timeout, withCredentials.

Requests are sent as application/x-www-form-urlencoded unless the data is FormData or you set a Content-Type header. A request without headers also sends api-key: secret-key. The last request is available as fr.xhr.instance.

Presets

fr.xhr.configure() sets options that every request uses, including form submits:

fr.xhr.configure({
    headers: {"api-key": apiToken},
    data: {csrf: csrfToken},
    timeout: 30000
});
  • Each call adds to the existing presets. fr.xhr.configure() with no argument returns them.
  • Options passed to a request win. Headers are matched by name regardless of case, and preset data is merged with the request's data (objects by key; FormData and query strings get the missing fields).
  • fr.xhr.config = {...} also sets presets and takes priority over configure().

Hooks

fr.xhr.initdata = [{name: "csrf", value: token}];   // added to every request's data
fr.xhr.beforesuccess = function (response) { };     // runs before every success callback

Both can also be set with fr.regedit("initdata", [...]) and fr.regedit("beforesuccess", fn).

Plugins

fr.plugin(name, fn) adds a method to every FrostJS object. Inside fn, this is the FrostJS object:

fr.plugin("highlight", function (color) {
    this.css("background-color", color || "yellow");
    return this;
});

fr("#note").highlight();

Plugins attach to FrostJS objects created after the plugin is registered. The fr.name shortcuts are created when the page loads, so call fr.refresh() after adding a plugin if you use them, or register plugins before the page finishes loading.

A plugin never replaces an existing method. If the name is already used by a built-in method or another plugin, the plugin is not added, a warning is logged and fr.plugin() returns false.

Overriding a method

To replace a built-in method or a plugin on purpose, use fr.override(target, fn). target is the method name or the method itself. Inside fn, this.<name>.original(...) calls the method being replaced:

fr.override("data", function (name, value) {
    return this.data.original(name, value);
});

fr.override(fr("#note").highlight, function () {
    return this.highlight.original("lightblue");
});

Overrides stack: each one wraps the previous one. fr.override() returns false if there is no method by that name.

Other functions

Function Description
fr.regedit(path, value) Stores a value in the shared registry; without value, reads it. Paths use ->, e.g. fr.regedit("user->name", "Jo"). fr.regedit("*") returns the whole registry.
fr.cookie(name) Reads a cookie; returns false when it is not set.
fr.cookie(name, value, maxAge, sameSite) Sets a cookie. maxAge is in seconds (1 day by default), sameSite is "Lax" by default.
fr.num(value) Extracts a number from a string: fr.num("$1,250.50") → 1250.5.
fr.formatNum(value) Formats a number with two decimals and thousands separators: "1,250.50".
fr.split(value, delimiter, removeEmpty) Splits a string, dropping empty parts unless removeEmpty is false.
fr.serialise(object) Serialises an object into a query string.
fr.getObjectSize(object) The number of own properties.
fr.noConflict() Points window.fr to FrostJS and returns a function that restores the previous fr.

What's new in 1.4

  • XHR presets: fr.xhr.configure() and the config option.
  • Form XHR: fr(form).configure({...}).xhr({...}).then(...).catch(...).
  • npm modules: import/require support for bundlers and Node, and TypeScript types.
  • fr.override(), and plugins can no longer silently replace built-in methods or other plugins.
  • Events: handlers for input, message and composition events no longer crash, and e.data() works on every event.
  • Fixes:
    • fr(function) runs immediately when the page has already loaded.
    • .off() removes listeners properly.
    • .visible() reads the computed style.
    • .append()/.prepend() keep order and copy content to every target.
    • .parent(selector) skips elements without a match.
    • Cookies are encoded and default to SameSite=Lax.
    • fr.create() copies the element it is given.
    • XHR serialises nested data and sends GET data in the query string.
    • Reassigning an array with fr.regedit() or fr.xhr.initdata replaces it instead of keeping the old entries, so rotating CSRF tokens are sent correctly.

License

ISC

Related Packages

@agnostico/browser-solidity-compiler

Solidity compiler that works in the browser

1,548 18
waelio-utils

Helper Utils often used in websites

8,166 1
portfolio-github-integration

Browser-native library to fetch GitHub repositories and custom metadata for dyna...

1,360
@vaultdrop/sdk

VaultDrop SDK for browsers — Web Crypto AES-GCM and API client for React, Vue, A...

1,147
@abarchibody/storage

Storage is plugin that help to store data in the local storage, or retrieve data

935

Version History

Version Released License
1.4.1 ISC
1.3.12 ISC
1.2.21 ISC
1.2.11 ISC
1.2.10 ISC