almostnojs
Version:
A minimalist, dependency-free JavaScript framework featuring tagged template rendering, DOM morphing, custom elements, state management, event handling, animations, and HTTP requests.
449 lines (337 loc) • 14.4 kB
Markdown
# AlmostNo.js




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.
## Features
- **Tagged Template Rendering** – Lit-style `html` tagged templates with surgical DOM updates.
- **DOM Morphing** – Reconcile live DOM against new HTML without destroying state.
- **Custom Elements** – Reactive `AnJSElement` base 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.
## Examples
See AlmostNo.js [Live Examples](https://coreyolson.github.io/almostno.js/) in action.
- [Animations](docs/animate.html)
- [Attributes](docs/attributes.html)
- [Components](docs/components.html)
- [Core Features](docs/core.html)
- [DOM Manipulation](docs/dom.html)
- [Elements](docs/elements.html)
- [Events](docs/events.html)
- [Filtering & Traversal](docs/filtering.html)
- [HTTP Requests](docs/request.html)
- [State Management](docs/state.html)
- [Utilities](docs/utilities.html)
## Browser Support
AlmostNo.js targets **ES2020** and works on all modern browsers (Chrome, Firefox, Edge, Safari, Opera).
## Installation
### NPM
```sh
npm install almostnojs
```
```js
import $, { html, render, morph, AnJSElement, registerComponent } from 'almostnojs';
```
### CDN
```html
<!-- 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>
```
### Self-hosting
Download the latest release from `dist/browser/` and include it directly:
```html
<script src="./almostno.full.js"></script>
```
## Choosing the Right Version
Three prebuilt bundles are available, plus NPM with tree-shaking.
### Feature Comparison
| 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** | ❌ | ❌ | ❌ | ✅ |
### Bundle Sizes (minified + gzipped)
| 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` |
## Quick Start
### Template Parts (Tagged Templates)
Render reactive templates with surgical DOM updates — only changed values are patched.
```js
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 node
```
#### Attributes
```js
render(html`<div class="card ${active ? 'active' : ''}">
<button disabled=${!ready}>Submit</button>
</div>`, container);
```
#### Lists
```js
const items = ['Apple', 'Banana', 'Cherry'];
render(html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>`, container);
```
#### Unsafe HTML
```js
import { html, unsafeHTML, render } from 'almostnojs';
render(html`<div>${unsafeHTML('<em>trusted markup</em>')}</div>`, container);
```
### DOM Morphing
Reconcile live DOM against new HTML without destroying state, focus, or event listeners.
```js
import { morph } from 'almostnojs';
morph(document.getElementById('app'), '<div class="updated">New content</div>');
```
### Custom Elements
Build reactive web components with `AnJSElement`.
```js
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);
```
```html
<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.
#### Keyed Lists with `repeat()`
```js
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);
```
#### Auto-Cleanup
```js
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>`; }
}
```
#### Awaiting Updates
```js
const el = document.querySelector('my-counter');
el.state.count = 42;
await el.updateComplete;
// DOM is now updated
```
### DOM Manipulation
```js
$('div').text('Hello, World!');
$('#box').css('color', 'red').class('highlight');
```
### Events
```js
$('#btn').on('click', () => alert('Clicked!'));
$.emit('app:ready', { ts: Date.now() });
$.listen('app:ready', data => console.log(data));
```
### State Management
```js
const state = $('#app').state({ count: 0 });
$('#increment').on('click', () => state.count++);
$('#display').bind(state);
```
```html
<div id="app">
<span id="display" data-bind="count"></span>
<button id="increment">+1</button>
</div>
```
### Components
```js
$.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++ } })
);
```
```html
<Card title="Hello"></Card>
```
### HTTP Requests
```js
$.get('/api/data').then(console.log);
$.post('/api/submit', { name: 'Jane' });
$.get('/api/slow', { timeout: 3000 });
```
## API Reference
### Template Parts
| 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) |
### DOM Morphing
| Function | Description |
|----------|-------------|
| `morph(target, newHTML)` | Reconcile live DOM to match new HTML string |
### Custom Elements
| 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 |
#### `AnJSElement` Instance API
| 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 |
### Core
- `$(selector)` – Select elements.
- `$.extend(name, func, force)` – Extend AlmostNo.js.
### Iteration
- `.each(fn)` – Iterate over matched elements.
- `.get(index)` – Get an element by index.
- `.clone(deep)` – Clone an element.
### DOM Manipulation
- `.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.
### Attributes
- `.id(value)` – Get/set the `id` attribute.
- `.attr(name, value)` – Get/set attributes.
- `.removeAttr(name)` – Remove an attribute.
- `.serialize()` – Serialize a form.
### Events
- `.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.
### Event Bus
- `$.emit(event, data)` – Emit a global event.
- `$.listen(event, handler)` – Listen for a global event.
- `$.forget(event, handler)` – Remove a global event listener.
### Traversal
- `.next()` / `.prev()` – Adjacent siblings.
- `.parent()` – Parent element.
- `.children()` – Direct children.
- `.siblings()` – All siblings.
- `.closest(selector)` – Closest matching ancestor.
### Filtering
- `.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.
### Animations
- `.animate(styles, duration, easing)` – Animate CSS properties.
- `.fade(opacity, duration)` – Fade to a specific opacity.
- `.fadeIn(duration)` / `.fadeOut(duration)` – Fade in/out.
### State Management
- `.state(initialState)` – Create a reactive state proxy.
- `.bind(state, context)` – Bind state to DOM via `data-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.
### HTTP Requests
- `$.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.
### Utilities
- `$.json(string)` – Safe JSON parse (returns `null` on 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.
### Event Aliases
- `.click(cb)` / `.change(cb)` / `.submit(cb)` / `.keydown(cb)` / `.keyup(cb)` / `.mouseover(cb)` / `.mouseout(cb)` – Event shortcuts.
## Why AlmostNo.js?
- **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.
## License
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.