Manual

Request elements

The idea behind htmlact is to allow any HTML element to perform requests on the server for HTML fragments. These requests are specified via the data-request attribute. We call request elements, elements that bear such an attribute.

In the following example:

<button data-request="POST /clicked"
        data-effect="element">Replace me</button>

The button is a request element. The request is performed when the event specified via the data-event attribute occurs. For buttons, if unspecified, this is the click event.

What happens exactly on a request is fully specified by the connection cycle which we describe next.

Connection cycle

Each request element defines a connection cycle in which a few other elements take part. All these other elements are always defined via attributes of the request element. They can all point to the same element. In particular if they are not specified they mostly default to the request element itself.

Every request element on the page cycle through the following steps.

  1. Wait for the element's request event to happen.
  2. Determine from the element's attributes, the kind of request, its query data, the target element, the effect and the feedback element(s).
  3. Perform the request with the query. Redirections are followed. The request and feedback elements get classified as htmlact-request
  4. Wait for the response. If an error occurs classify the request and feedback elements with htmlact-error and stop here. Otherwise unclassify htmlact-request and proceed.
  5. Process any special header in the response. This may stop the cycle here (e.g. on reload or redirect).
  6. Using the HTML fragment found in the server response, perform the effect on the target. If elements are being removed from the page, they first get classified with htmlact-out for a while before being effectively removed. Elements that are introduced get transiently classified with htmlact-in.
  7. If the request is:

    • A websocket, loop to 1 with the same socket.
    • An SSE connection, loop to 4.
    • Otherwise loop to 1 (if the element still exists after the effect).

Selector syntax

Attributes of htmlact use CSS selectors with querySelector to gather query data and target response effects. The selector is applied on the request element, however CSS selectors have no syntax for addressing ancestors. We slightly extend the syntax to allow it because in many cases this eschews the need to use element identifiers which improves modularity – use with care, it can also improve obscurity.

We allow a CSS selector to be prefixed by a sequence of ancestor specifications which select the root on which the CSS selector is then applied. This provides full tree addressing: first move up to find an ancestor and then down by applying the CSS selector to it.

An ancestor specification is made of an optional element, optional classes and the made up :up pseudo-class. The semantic is to move up from the element by following the up selectors from left-to-right and then apply the CSS selector on ancestor that was found.

Examples:

  :scope > *         # children   (regular CSS selector)
  :scope *           # descendent (regular CSS selector)
  :up                # parent
  :up :up            # parent's parent
  :up :up :scope > * # parent's parent's children
  .beet.root:up      # ancestor with classes beet and root
  .beet:up .root:up  # move to beet ancestor, then to root ancestor
  ul.beet.root:up    # ul ancestor with classes beet and root
  div:up .beet       # div ancestor, beet classified descendents

The full syntax as an RFC 5234 ABNF grammar is described as follows:

         sel = *(up SP) css-selector
          up = [[el] *class] ":up"
          el = 1*(ALPHA)
       class = "." 1*(ALPHA)
css-selector = …   # See the CSS specification

CSS class feedback

These classes track connection cycle states.

htmlact-error

This class is applied on the request element and the feedback element whenever a request failed. This includes if the request returns a 4XX or 5XX response.

htmlact-in and htmlact-in-parent

These classes are applied for a small amount of time (one render frame) when the effect is performed:

These classes are used to trigger CSS animations. For example to fade in an element in 250ms on insertion use:

.my-element.htmlact-in { opacity: 0; }
.my-element { opacity: 1; transition: opacity 250ms; }

htmlact-out and htmlact-out-parent

These classes are applied on elements (if any) that are removed by the effect just before it is performed:

To determine the amount of time during which the classes are applied before the effect is performed, first the classes are applied on the elements. Then for each of these elements we take the maximum value of either CSS attribute --htmlact-out-duration, animation-duration or transition-duration. The maximal value of all these durations defines the duration.

If the elements animating are descendent of those that are classified by htmlact-out or htmlact-out-parent, use the --htmlact-out-duration attribute on the .htmlact-out elements to set the time needed. For example the following ensures that the htmlact-out and htmlact-out-parent classes are maintained on the elements to be removed for 500ms before being effectively removed:

.htmlact-out { --htmlact-out-duration: 500ms }

htmlact-request

This class is applied on the request element and the feedback element(s) whenever a request to the server is ongoing.

This class can be used to trigger a CSS animation if the request is taking too much time. For example assuming you have a .spinner in your request element, the following shows the spinner if the request is taking longer than 500ms.

.spinner { visibility: hidden; }
.htmlact-request .spinner { animation: spin 1s linear 500ms infinite; }

@keyframes spin
{ from { visibility: visible; } to { transform: rotate(360deg); }}

Attribute reference

The only attribute required for an HTML element to be connected to your server is data-request which specifies the request to initiate the connection.

data-effect="<eff>"

The value <eff> determines the way the HTML response is used on the target. This can be:

If unspecified this is children.

data-event="<ev> <mod>*"

<ev> is the name of the JavaScript event of the event source that triggers requests. <mod> are modifiers for the event:

If the attribute is unspecified this is:

data-event-src="<sel>"

The DOM element(s), as selected by <sel>, whose events are being listened for to trigger a request. If unspecified this is the request element itself.

data-feedback="<sel>"

DOM elements in addition to target element on which the connection cycle is feedback with htmlact-request and htmlact-error.

data-query="<sel>"

The value of <sel> is a selector which, when applied on the request element, determines the elements that are used to specify the query of the request. If unspecified this is the request element itself. See the syntax for selectors.

The final query of the request is determined by taking the query part of the requested URL and for each of the elements selected by <sel> appending:

The query is transmitted in the URL on GET or HEAD requests and otherwise in the request body encoded with application/x-www-form-urlencoded unless there is a file input in which case multipart/form-data is used.

data-query-rescue="<bool>|force"

Use this to prevent users from leaving a page without performing the request if the query data changed.

If true, the values determined by data-query are snapshot on DOM insertion. If they still exist and they changed when the beforeunload window event triggers the browser specific "leave page confirmation" modal is triggered.

If force, this forces the event to trigger (e.g. if you performed validation on the server and put it back on page).

data-referrer-policy="<policy>"

The value of <policy> determines the referrer-policy with which the request is made. It determines the value of the request's referer header. If unspecified this is strict-origin.

data-request="[<m>] <url>"

This attribute drives it all. If unspecified the element has no connection to the server and all other data-* attributes are ignored.

<url> is the URL to request, use the ws:// or wss:// scheme for a websocket connection. <m> is the HTTP request method or SSE to request a server sent events connection; defaults to GET if unspecified.

data-target="<sel>"

The target DOM element, as selected by the first element of <sel>, on which the request response performs its effect. If unspecified this is the request element itself.

Request HTTP headers

These headers are automatically added on requests made by the request elements.

htmlact: true

Indicates the request is made by the htamlact page driver.

referer: <url>

This is the standard HTTP referer header. It indicates the URL of the page (without the fragment) that performs the request. Requests are made according to the data-referrer-policy attribute of the request element.

If the HTML fragment of your response has relative URLs, they will be interpreted relative to <url> once they are injected in the DOM.

Response HTTP headers

The server should respond with an HTML fragment. This fragment is inserted in the DOM according to the data-target and data-effect values of the request attribute.

Additionally the response can use the following headers to control the client.

htmlact-location-push: <url>

Pushes location <url> on the history stack.

htmlact-location-replace: <url>

Replaces current location by <url> (without reloading). Has no effect if htmlact-location-push is present.

htmlact-location-title: <pct-title>

Replaces the current title by the percent encoded title <pct-title>. Mostly used in conjunction with htmlact-location-push or htmlact-location-replace.

htmlact-redirect: <url>

Redirects the page to <url>. The body of the response is ignored by htmlact page driver.

htmlact-reload: true

Reloads the page. The body of the response is ignored by the htmlact page driver.