AlmostNo.js is a lightweight, zero-dependency JavaScript framework featuring tagged template rendering, DOM morphing, custom elements, reactive state management, chainable DOM manipulation, event handling, animations, and HTTP requests.
- Tagged Template Rendering – Lit-style
htmltagged templates with surgical DOM updates. - DOM Morphing – Reconcile live DOM against new HTML without destroying state.
- Custom Elements – Reactive
AnJSElementbase class with batched updates and computed properties. - Tiny & Fast – 5 KB core, 6 KB extended, 12 KB full (minified + gzipped). Zero dependencies.
- Chainable API – Familiar
$().method()syntax for clean, readable code. - DOM Manipulation – Select, traverse, and modify elements effortlessly.
- Events & Event Bus – Attach, delegate, trigger events; cross-component communication.
- Reactive State – Proxy-based state with
onChange,onAny,patch, and DOM bindings. - Components – SSR-compatible server/client components with auto-mounting.
- HTTP Requests – Fetch wrappers with timeout handling and abort controllers.
- Animations – Simple CSS-based transitions.
- Utilities – Debounce, throttle, type checks, JSON parsing, and more.
See AlmostNo.js Live Examples in action.
- Animations
- Attributes
- Components
- Core Features
- DOM Manipulation
- Elements
- Events
- Filtering & Traversal
- HTTP Requests
- State Management
- Utilities
AlmostNo.js targets ES2020 and works on all modern browsers (Chrome, Firefox, Edge, Safari, Opera).
npm install almostnojsimport $, { html, render, morph, AnJSElement, registerComponent } from 'almostnojs';<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/almostnojs@latest/dist/cdn/almostno.full.js"></script>
<!-- UNPKG -->
<script src="https://unpkg.com/almostnojs@latest/dist/cdn/almostno.full.js"></script>Download the latest release from dist/browser/ and include it directly:
<script src="./almostno.full.js"></script>Three prebuilt bundles are available, plus NPM with tree-shaking.
| Feature | Core | Extended | Full | NPM |
|---|---|---|---|---|
| DOM Manipulation | ✅ | ✅ | ✅ | ✅ |
| Events & Event Bus | ✅ | ✅ | ✅ | ✅ |
| Attributes | ✅ | ✅ | ✅ | ✅ |
| HTTP Requests | ✅ | ✅ | ✅ | ✅ |
| Animations | ❌ | ✅ | ✅ | ✅ |
| Filtering & Traversal | ❌ | ✅ | ✅ | ✅ |
| State Management | ❌ | ❌ | ✅ | ✅ |
| Components | ❌ | ❌ | ✅ | ✅ |
| Template Parts | ❌ | ❌ | ❌ | ✅ |
| DOM Morphing | ❌ | ❌ | ❌ | ✅ |
| Custom Elements | ❌ | ❌ | ❌ | ✅ |
| Version | Size | Path |
|---|---|---|
| Core | ~5 KB | dist/browser/almostno.js |
| Extended | ~6 KB | dist/browser/almostno.extended.js |
| Full | ~12 KB | dist/browser/almostno.full.js |
Render reactive templates with surgical DOM updates — only changed values are patched.
import { html, render } from 'almostnojs';
const app = document.getElementById('app');
function view(name) {
render(html`<h1>Hello, ${name}!</h1>`, app);
}
view('World'); // First render: creates DOM
view('AnJS'); // Update: patches only the text noderender(html`<div class="card ${active ? 'active' : ''}">
<button disabled=${!ready}>Submit</button>
</div>`, container);const items = ['Apple', 'Banana', 'Cherry'];
render(html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>`, container);import { html, unsafeHTML, render } from 'almostnojs';
render(html`<div>${unsafeHTML('<em>trusted markup</em>')}</div>`, container);Reconcile live DOM against new HTML without destroying state, focus, or event listeners.
import { morph } from 'almostnojs';
morph(document.getElementById('app'), '<div class="updated">New content</div>');Build reactive web components with AnJSElement.
import { AnJSElement, html, registerComponent } from 'almostnojs';
class MyCounter extends AnJSElement {
init() {
this.state.count = 0;
}
render() {
return html`
<button @click=${() => this.state.count++}>
Clicked ${this.state.count} times
</button>`;
}
}
registerComponent('my-counter', MyCounter);<my-counter></my-counter>Features: reactive state proxy, batched microtask updates, computed properties, observedAttributes reflection, init() / updated() / destroy() lifecycle hooks, auto-cleanup with this.own(), updateComplete promise, repeat() keyed lists.
import { AnJSElement, html, repeat, registerComponent } from 'almostnojs';
class TodoList extends AnJSElement {
init() {
this.state.items = [
{ id: 1, text: 'Learn AnJS' },
{ id: 2, text: 'Build something' },
];
}
render() {
return html`<ul>${repeat(
this.state.items,
item => item.id,
item => html`<li>${item.text}</li>`
)}</ul>`;
}
}
registerComponent('todo-list', TodoList);class LiveWidget extends AnJSElement {
init() {
// Automatically unsubscribed when element disconnects
this.own($.listen('data:update', (data) => {
this.state.value = data;
}));
}
render() { return html`<span>${this.state.value}</span>`; }
}const el = document.querySelector('my-counter');
el.state.count = 42;
await el.updateComplete;
// DOM is now updated$('div').text('Hello, World!');
$('#box').css('color', 'red').class('highlight');$('#btn').on('click', () => alert('Clicked!'));
$.emit('app:ready', { ts: Date.now() });
$.listen('app:ready', data => console.log(data));const state = $('#app').state({ count: 0 });
$('#increment').on('click', () => state.count++);
$('#display').bind(state);<div id="app">
<span id="display" data-bind="count"></span>
<button id="increment">+1</button>
</div>$.component("Card",
({ state, props }) => `
<div class="card">
<h3>${props.title}</h3>
<p>Likes: <span data-bind-this="likes"></span></p>
<button data-action="like">Like</button>
</div>`,
() => $.state({ likes: 0, like() { this.likes++ } })
);<Card title="Hello"></Card>$.get('/api/data').then(console.log);
$.post('/api/submit', { name: 'Jane' });
$.get('/api/slow', { timeout: 3000 });| Function | Description |
|---|---|
html`...` |
Tagged template literal — returns a TemplateResult |
render(result, container) |
Render a TemplateResult into a DOM element |
clearTemplate(container) |
Clear cached template data for a container |
unsafeHTML(string) |
Mark a string as trusted HTML (bypasses escaping) |
| Function | Description |
|---|---|
morph(target, newHTML) |
Reconcile live DOM to match new HTML string |
| Export | Description |
|---|---|
AnJSElement |
Base class for reactive custom elements |
registerComponent(name, cls) |
Register a custom element (idempotent) |
repeat(items, keyFn, templateFn) |
Keyed list helper for efficient DOM reconciliation |
| Member | Description |
|---|---|
state |
Reactive proxy — property writes trigger batched updates |
computed(name, deps, fn) |
Define a computed property |
render() |
Return html`...` or a string — called on every update |
update() |
Force a synchronous DOM update |
own(disposerFn) |
Register a cleanup function — called automatically on disconnect |
updateComplete |
Promise that resolves after the current render cycle |
static updateStrategy |
'microtask' (default) or 'raf' for frame-coalesced updates |
init() |
Lifecycle hook — called once after first render |
updated() |
Lifecycle hook — called after every render |
destroy() |
Lifecycle hook — called on disconnect before auto-cleanup |
$(selector)– Select elements.$.extend(name, func, force)– Extend AlmostNo.js.
.each(fn)– Iterate over matched elements..get(index)– Get an element by index..clone(deep)– Clone an element.
.content(value, html)– Get/set text or HTML content..text(value)– Get/set text content..html(value)– Get/set HTML content..css(prop, value)– Get/set CSS styles..class(name, add)– Add, remove, or toggle classes..display(show)– Show or hide elements..hide()/.show()– Convenience hide/show..remove()– Remove elements from the DOM..empty()– Remove all child elements..insert(content, position)– Insert elements at a position..append(content)/.prepend(content)– Insert content at start/end..before(content)/.after(content)– Insert adjacent content..prop(name, value)– Get/set DOM properties..val(value)– Get/set form element values..focus()/.blur()– Focus and blur.
.id(value)– Get/set theidattribute..attr(name, value)– Get/set attributes..removeAttr(name)– Remove an attribute..serialize()– Serialize a form.
.on(event, selector?, handler)– Attach event listeners (optional delegation)..off(event, selector?, handler)– Remove event listeners..delegate(event, selector, handler)– Delegated event listener..undelegate(event, selector, handler)– Remove delegated listener..trigger(event)– Dispatch an event.
$.emit(event, data)– Emit a global event.$.listen(event, handler)– Listen for a global event.$.forget(event, handler)– Remove a global event listener.
.next()/.prev()– Adjacent siblings..parent()– Parent element..children()– Direct children..siblings()– All siblings..closest(selector)– Closest matching ancestor.
.filter(callbackOrSelector)– Filter elements..find(selector)– Find descendants..first()/.last()– First or last element..even()/.odd()– Even or odd indexed elements..has(className)– Check for a class.
.animate(styles, duration, easing)– Animate CSS properties..fade(opacity, duration)– Fade to a specific opacity..fadeIn(duration)/.fadeOut(duration)– Fade in/out.
.state(initialState)– Create a reactive state proxy..bind(state, context)– Bind state to DOM viadata-bind..unbind(state)– Remove bindings.$.global(name, initial)– Create or retrieve global state.$.hasGlobal(name)– Check if a global state exists.$.clearGlobal(name)– Remove a global state.
$.get(url, options)– GET request.$.post(url, data, options)– POST request.$.put(url, data, options)– PUT request.$.delete(url, options)– DELETE request.$.patch(url, data, options)– PATCH request.$.head(url, options)– HEAD request.$.options(url, options)– OPTIONS request.$.abortController()– Create an AbortController.
$.json(string)– Safe JSON parse (returnsnullon failure).$.trim(string)– Trim whitespace.$.range(x, min, max)– Check if a number is within range.$.isFunction(v)/$.isObject(v)/$.isString(v)/$.isNumber(v)– Type checks.$.contains(parent, child)– Check DOM containment.$.debounce(fn, delay)– Debounced function.$.throttle(fn, limit)– Throttled function.$.element(tag, attrs, children)– Create an element.
.click(cb)/.change(cb)/.submit(cb)/.keydown(cb)/.keyup(cb)/.mouseover(cb)/.mouseout(cb)– Event shortcuts.
- Modern & Minimal – No legacy baggage. ES2020 modules with tree-shaking.
- Tagged Templates – Lit-inspired rendering without a build step.
- DOM Morphing – Efficient reconciliation without virtual DOM overhead.
- Custom Elements – First-class web component support.
- Reactive State – Proxy-based reactivity with automatic DOM updates.
- Easy to Learn – Familiar jQuery-style API with modern capabilities.
- Fast & Lightweight – 5–12 KB gzipped depending on bundle.
- Extensible – Add custom methods, components, and global state.
This project is licensed under the MIT License — see the LICENSE file for details.