paxi.js is an experimental, minimalist companion to fixi.js 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.
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:
<script src="fixi.js"></script>
<script src="paxi.js"></script>
<button fx-action="/profile"
fx-target="#profile"
fx-swap="morph">
Reload Profile
</button>
<div id="profile">
<input id="nickname" placeholder="typing here survives the swap">
<p id="bio">...</p>
</div>The response HTML replaces the contents of #profile, but the <input> keeps its focus,
caret, and any in-progress value because its id matches a node in the incoming markup.
paxi is to 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/afterNodeMorphedcallbacksouterHTMLvsinnerHTMLmodes- id-set propagation across ancestors
- pluggable node matchers
A hard constraint on the project is that the unminified, uncompressed size stays under 2KB.
Drop paxi.js in a script tag after (or before) fixi.js:
<script src="fixi.js"></script>
<script src="paxi.js"></script>Or install via npm:
npm install paxi-js
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.
morph(document.getElementById("panel"), "<div id='panel'>new contents</div>")When loaded alongside fixi, paxi registers a config hook that intercepts fx-swap="morph"
and wires it to morph(cfg.target, cfg.text).
paxi walks the old and new trees in lockstep:
- 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.
- For text and comment nodes, the value is updated when it differs.
- For elements, attributes are synced (extras removed, missing added, values updated).
- Children are reconciled positionally, except that id-keyed children in the incoming tree pull their original counterparts forward when they exist.
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.