UNPKG

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
# AlmostNo.js ![Version](https://img.shields.io/github/v/tag/coreyolson/almostno.js?label=version) ![License](https://img.shields.io/github/license/coreyolson/almostno.js) ![Minified + Gzip](https://img.shields.io/bundlephobia/minzip/almostnojs) ![Flexible & Easy to Learn](https://img.shields.io/badge/flexible-Easy%20to%20Learn-gold) 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.