♻️ paxi.js - a clever little diff...

paxi.js is an experimental, minimalist companion to [fixi.js](https://github.com/bigskysoftware/fixi) that swaps HTML into the DOM by *morphing* instead of replacing: preserving existing nodes, focus, scroll position, and form state wherever the old and new trees align. Part of the [fixi project](https://fixiproject.org). When paired with fixi, paxi registers itself as the `morph` swap strategy, so you can write `fx-swap="morph"` and get id-keyed reconciliation out of the box. It also works stand-alone via the `window.morph(target, html)` global. Here is an example: ```html

...

``` The response HTML replaces the contents of `#profile`, but the `` keeps its focus, caret, and any in-progress value because its `id` matches a node in the incoming markup. ## Minimalism paxi is to [idiomorph](https://github.com/bigskysoftware/idiomorph) what fixi is to htmx: a smaller, less ambitious take on the same idea. No configuration, no callbacks, no head-merging, no id-set inference - just a single recursive diff that keeps id-keyed nodes stable as the tree around them changes. As such, it does *not* include many features found in idiomorph or morphdom: * head merging / stylesheet reconciliation * `beforeNodeMorphed` / `afterNodeMorphed` callbacks * `outerHTML` vs `innerHTML` modes * id-set propagation across ancestors * pluggable node matchers A hard constraint on the project is that the *unminified, uncompressed* size stays under 2KB. ## Installing Drop `paxi.js` in a script tag after (or before) `fixi.js`: ```html ``` Or install via npm: ``` npm install @bigskysoftware/paxi-js ``` ## API ### `window.morph(target, html)` Morph `target` (an `Element`) toward the first element parsed from `html` (a string). The target is updated in place when the root node names match; otherwise it is replaced. ```js morph(document.getElementById("panel"), "
new contents
") ``` ### `fx-swap="morph"` When loaded alongside fixi, paxi registers a config hook that intercepts `fx-swap="morph"` and wires it to `morph(cfg.target, cfg.text)`. ## Modus Operandi paxi walks the old and new trees in lockstep: 1. If node type or name differ, the old node is replaced - but any id-keyed descendant of the incoming node whose id matches a node in the old tree is first rescued into place. 2. For text and comment nodes, the value is updated when it differs. 3. For elements, attributes are synced (extras removed, missing added, values updated). 4. Children are reconciled positionally, except that id-keyed children in the incoming tree pull their original counterparts forward when they exist. ## LICENCE ``` Zero-Clause BSD ============= Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ```