AJAX

Mode

Skin

Theme

Overview & Philosophy

ln-ajax is applied to containers, links, or forms via data-ln-ajax. It intercepts same-origin anchor clicks and form submissions via e.preventDefault() and issues asynchronous requests with X-Requested-With: XMLHttpRequest headers. When the server responds with targeted HTML fragments (such as { content: { "#target": "<html>" } }), ln-ajax replaces those DOM regions in-place and updates the browser history via history.pushState(), enabling SPA-speed page transitions while preserving server-rendered markup.

In ln-ashlar, this navigation and swapping primitive avoids client-side template bloat by letting the server remain the authoritative source for markup.

The Swapping Doctrine

An AJAX primitive has no internal routes, layout schemas, or database caches. It expects the server to act as the single source of truth for pages, returning structured JSON payloads containing page titles, DOM target selectors, and new HTML chunks to inject. The server-rendered HTML fragment is the final contract.

Three Orthogonal Layers of the Navigation Lifecycle

  1. The Navigation Trigger (Native HTML Links & Forms): Elements declare AJAX capabilities declaratively using the data-ln-ajax attribute. Clicking a link or submitting a form initiates the asynchronous lifecycle.
  2. The Transport Layer (AJAX Request): Handles the network exchange: adding CSRF token headers, preparing FormData, checking authorization boundaries, and communicating with server endpoints.
  3. The DOM Reconciliation Layer (Page Swap): Receives the server's structured JSON payload. It parses HTML fragments, matches target selectors (e.g. replacing `#main-content`), updates page titles, and commits the address bar state via browser History APIs.

How the Page Swapping Flows

  1. A user clicks an AJAX-marked link. The component intercepts the event, adds the loading class .ln-ajax--loading to the container, and dispatches a bubbled ln-ajax:before-start event.
  2. It fires a background GET request. The server intercepts the request and responds with a JSON payload specifying which parts of the page to replace.
  3. The component swaps the HTML segments in-place, updates the browser's address history, dispatches `ln-ajax:success`, and clears loading classes. Newly loaded forms are immediately upgraded.

For the full guide see the ln-ajax README and the architecture reference.

Basics

AttributeOn elementDescription
data-ln-ajaxcontainer, <a>, <form>Activates AJAX handling
data-ln-ajax="false"<a>, <form>Exclude a specific element from AJAX

AJAX Links

Behavior

  • Click on a link → GET AJAX request to href
  • Ctrl/Cmd+Click → Opens in a new tab (normal)
  • Middle click → Opens in a new tab (normal)
  • Links with # → skipped
  • After a successful response → URL is added to browser history (pushState)

HTML example

<div data-ln-ajax>
    <a href="/users">Users</a>
    <a href="/settings">Settings</a>

    <!-- Exclude a specific link -->
    <a href="/login" data-ln-ajax="false">Log in</a>
</div>

AJAX Forms

Behavior

  • Submit → AJAX request (method and action from the form)
  • FormData → automatically created
  • Form buttons → disabled during the request
  • GET forms → parameters in URL + pushState
  • POST/PUT/DELETE → body is FormData

HTML example

<form data-ln-ajax method="POST" action="/api/users">
    <input type="text" name="name" placeholder="Name">
    <input type="email" name="email" placeholder="Email">
    <button type="submit">Save</button>
</form>

JSON Response Protocol

The server returns JSON with the following structure:

{
    "title": "New page title",
    "content": {
        "main-content": "<h1>Content</h1>",
        "sidebar-nav": "<ul><li>...</li></ul>"
    },
    "message": {
        "type": "success|error|warn|info",
        "title": "Title",
        "body": "Message",
        "data": {}
    }
}
FieldDescription
title<title> attribute of the page
contentObject: selector → HTML for replacement
messageMessage — available in the ln-ajax:success event

CSS classes for loading state

During request

The element receives the class .ln-ajax--loading. Use it for visual feedback.

SCSS example

a.ln-ajax--loading {
    opacity: 0.6;
    pointer-events: none;
}

button.ln-ajax--loading {
    cursor: wait;
}

CSRF Token

The component automatically reads the CSRF token from <meta name="csrf-token"> and sends it in the header:

<head>
    <meta name="csrf-token" content="...">
</head>

// ln-ajax reads it and sends it as:
// X-CSRF-Token: ...

API

ln-ajax has no public API — it is activated automatically via data attributes.

// Manual initialization of a new DOM element
window.lnAjax(document.getElementById('new-container'));

Events

Lifecycle events

EventWhendetail
ln-ajax:before-startBefore everything (cancelable — can cancel request){ method, url }
ln-ajax:startAfter adding spinner, before fetch{ method, url }
ln-ajax:successAfter successful response{ method, url, data }
ln-ajax:errorAfter fetch error{ method, url, status, data, error }
ln-ajax:completeAfter completion (success or error){ method, url }
ln-ajax:abortedA newer GET superseded this in-flight GET (instead of success/error/complete){ method, url }

Events are dispatched on the element that initiated the request (<a> or <form>) and bubble up.

Layer 2 Coordination & Toast Integration

ln-ajax stays isolated and emits ln-ajax:success and ln-ajax:error. The Layer 2 coordinator (ln-ui-coordinator) intercepts these bubbling events, inspects the response data.message envelope, and dispatches ln-toast:enqueue on window.

// Cancel a request conditionally (ln-ajax:before-start is cancelable)
document.addEventListener('ln-ajax:before-start', function(e) {
    if (!userIsAuthenticated()) {
        e.preventDefault(); // request is cancelled, no spinner
    }
});

Other examples

// Global loading indicator
document.addEventListener('ln-ajax:start', function(e) {
    document.body.classList.add('loading');
});
document.addEventListener('ln-ajax:complete', function(e) {
    document.body.classList.remove('loading');
});

// Refresh components after DOM change
document.addEventListener('ln-ajax:success', function(e) {
    window.lnSelect && window.lnSelect.reinit();
});

// Error handler
document.addEventListener('ln-ajax:error', function(e) {
    console.error('AJAX failed:', e.detail.url, e.detail.error);
});