Startr Swap
Startr Swap swaps links and forms in place, with no build step. Drop it in, mark a region, and same-origin links stop reloading the page: they swap in place instead, wrapped in a CSS View Transition.
It fetches the page and parses it with fetch + DOMParser. There is no
hidden iframe. An iframe performs a REAL navigation, so scripts in the response execute inside
it, in the wrong document, silently, and every swap re-requests the page's stylesheets.
fetch + DOMParser parses inert, and reports a redirect honestly.
Lineage is htmz (MIT, © Lean Rada), whose hash-names-the-target convention it keeps.
Install
<script defer src="https://startr.style/v1/swap.js"
integrity="sha384-8Y08Zw/VDvaVCy/KtaWr74UVCwN9nFkiMksvlmcQyQVecrpdmPkOH84i4aaIN6DX"
crossorigin="anonymous"></script>
<main data-swap> ... </main>
/v1/swap.js is immutable and cached for a year. /swap.js is the rolling
build this site uses itself and carries no integrity guarantee. The sha256 is published at
/v1/swap.js.sha256.
Markup
The API is three attributes and four events, and that is the whole thing.
| Attribute | Placed on | Meaning |
|---|---|---|
data-swap |
a region | Marks it swappable. A value narrows which links are taken over to a path prefix, and nested regions inherit it. |
data-swap-target |
a control | Updates THAT region, by selector, instead of the one this control lives in. |
data-swap-off |
a control | Never swaps this one. |
Only the region swaps; everything outside it stays. A page whose chrome differs outside the
region should not declare one, and links into it should carry data-swap-off. This
site does exactly that for its home page, whose hero sits outside <main>.
Events
All four fire on the region, bubbling, cancelable.
swap:before— cancel to abandon the swap; the browser navigates normally.swap:navigate— cancel to own the address bar yourself.swap:after— content is in.swap:error— cancel to suppress the fallback navigation.
Regions nest, so sub-swaps cost no syntax at all. A pager inside a region updates that region
and leaves the page alone, declaring nothing. A sub-swap region does need an id,
because the response has to be able to name the piece coming back.
Things every consumer hits
- There is no
<main>default, deliberately. The first version claimed<main>when no region was declared. Any document can end up running this file — a host application that adopts a fetched page's scripts adopts this one too — and a<main>default would then take over every link in a document that never asked for it. A region must be declared. - The click listener CAPTURES; the submit listener does not. A single-page router binds its own document-level click listener at boot, so in the bubble phase it always wins. Capture puts a declared region ahead of a generic router. Submit stays in the bubble phase on purpose, because attribute-driven libraries bind submits on the FORM, and capturing would take their submits away from them.
- A POST that redirected must push history; a POST that answered in place must
not. Post/Redirect/Get lands on a plain address a reader can reload and share. A POST
that answered in place has an address that only accepts POST, so pushing it hands the reader a
reload that 405s.
res.redirectedis the exact signal. - An inline
<script>in a swapped region never runs. The library re-runs onlyscript[src]. Re-create inline scripts from the host, as this site does insrc/_includes/swap-host.njkwith a MutationObserver. - Under a View Transition,
swap:afterfires before the update callback has run, so it may see the old content. Observe the region instead. Fix planned for v1.1.
Size
13,802 bytes raw, 5,343 gzipped (as the gate measures it). htmx is 50,917 raw and 16,367 gzipped. The gate
scripts/gates/startr-swap/check.py holds a ceiling of 8,192 gzipped.
Demo
/demo/swap/a.html and b.html swap between each
other.
/demo/swap/none.html loads the script, declares no region,
keeps a <main> on purpose, and must navigate normally.
Licence
MIT, the same as the framework. Source: https://github.com/Startr/Style.