Hyper · htmx-style

Request lifecycle events

Every Hyper request announces itself: kj:before, kj:after, kj:error, then kj:load once new content is in place. They bubble, so one listener on document sees them all.

EventFires onWhendetail
kj:beforerequesting elementbefore the request{ url, verb }
kj:afterrequesting elementresponse received, before the swap{ url, verb, html }
kj:errorrequesting elementnetwork error or non-2xx status{ url, verb, error } (error.status)
kj:loadtarget (or its parent, if outerHTML replaced it)after the swap{ url, verb }

Live · A page-wide progress bar from one listener

Responses land here.
View the source for this demo
HTML
<div class="lc-bar" aria-hidden="true"><div id="lc-progress"></div></div>
<div class="row">
  <button type="button" kj-get="/api/quote.html" kj-target="#lc-target">Succeeds</button>
  <button type="button" kj-get="/api/slow.php?ms=1400" kj-target="#lc-target">Slow (1.4 s)</button>
  <button type="button" kj-get="/api/fail.php?code=500" kj-target="#lc-target">Fails (500)</button>
</div>
<div id="lc-target" class="plate" aria-live="polite" style="box-shadow:none;margin-top:12px;min-height:3.2em">Responses land here.</div>
<pre class="out" id="lc-log"></pre>
JavaScript
const t0 = performance.now();
const log = (name, text) => $('#lc-log').text(
  ('+' + Math.round(performance.now() - t0) + 'ms  ' + name.padEnd(10) + text + '\n' + $('#lc-log').text()).split('\n').slice(0, 9).join('\n'));
let inflight = 0;

$(document).on('kj:before', (e, d) => {
  inflight++;
  $('#lc-progress').css({ opacity: 1, width: '70%' });
  log('kj:before', d.verb.toUpperCase() + ' ' + d.url);
});
$(document).on('kj:after', (e, d) => log('kj:after', d.url + ' (' + d.html.length + ' bytes)'));
$(document).on('kj:error', (e, d) => {
  log('kj:error', d.url + ' → HTTP ' + d.error.status);
  $('#lc-target').text('Something went wrong (HTTP ' + d.error.status + '). Try again.');
});
$(document).on('kj:load', e => log('kj:load', '#' + e.target.id + ' updated'));

$(document).on('kj:after kj:error', () => {
  if (--inflight > 0) return;
  $('#lc-progress').css('width', '100%');
  setTimeout(() => $('#lc-progress').css({ opacity: 0, width: '0%' }), 450);
});

More examples

Global error toast

JavaScript
$(document).on('kj:error', (e, { url, error }) => {
  showToast(error.status === 401 ? 'Please sign in again' : 'Request to ' + url + ' failed');
});

API

kj:before · kj:after · kj:error · kj:load

kj:before (before the request), kj:after (response received, before the swap; detail.html) and kj:error (detail.error.status) fire on the requesting element. kj:load fires on the target after the swap — or on its former parent when outerHTML replaced it. All bubble.

Reference: Lifecycle events →

Migrating

Coming from htmxRough equivalents: htmx:beforeRequest → kj:before, htmx:afterRequest → kj:after, htmx:responseError → kj:error, htmx:afterSwap → kj:load. Events are not cancelable.

Related features