move general extension docs back to htmx.org/extensions and include core extension documentation on the site

This commit is contained in:
Carson Gross 2024-10-03 18:52:59 -06:00
parent f4e67863d9
commit 5b2fe2c19c
43 changed files with 1125 additions and 51 deletions

View File

@ -8,6 +8,7 @@
* Better degredation of `hx-boost` on forms with query parameters in their `action`
* Improved shadowRoot support
* Many smaller bug fixes
* Moved the core extension documentation back to <https://htmx.org/extensions>
## [2.0.2] - 2024-08-12
* no longer boost forms of type `dialog`

View File

@ -17,7 +17,7 @@ directly in HTML, using [attributes](https://htmx.org/reference#attributes), so
htmx is small ([~14k min.gz'd](https://unpkg.com/htmx.org/dist/)),
[dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json) &
[extendable](https://extensions.htmx.org/)
[extendable](https://htmx.org/extensions)
## motivation

2
dist/ext/README.md vendored
View File

@ -4,6 +4,6 @@ These are legacy extensions for htmx 1.x and are **NOT** actively maintained or
They are here because we unfortunately linked to unversioned unpkg URLs in the installation guides for them
in 1.x, so we need to keep them here to preserve those URLs and not break existing users functionality.
If you are looking for extensions for htmx 2.x, please see the [htmx 2.0 extensions site](https://extensions.htmx.org),
If you are looking for extensions for htmx 2.x, please see the [htmx 2.0 extensions site](https://htmx.org/extensions),
which has links to the new extensions repos (They have all been moved to their own NPM projects and URLs, like
they should have been from the start!)

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('ajax-header', {
onEvent: function (name, evt) {

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('alpine-morph', {
isInlineSwap: function (swapStyle) {

View File

@ -2,7 +2,7 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
function splitOnWhitespace(trigger) {

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('client-side-templates', {
transformResponse : function(text, xhr, elt) {

2
dist/ext/debug.js vendored
View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('debug', {
onEvent: function (name, evt) {

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
// Disable Submit Button
htmx.defineExtension('disable-element', {

View File

@ -1,7 +1,7 @@
(function(){
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
function stringifyEvent(event) {
var obj = {};

View File

@ -7,7 +7,7 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
var api = null;

View File

@ -1,7 +1,7 @@
(function(){
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
function mergeObjects(obj1, obj2) {

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('json-enc', {
onEvent: function (name, evt) {

View File

@ -2,7 +2,7 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
let loadingStatesUndoQueue = []

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('method-override', {
onEvent: function (name, evt) {

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('morphdom-swap', {
isInlineSwap: function(swapStyle) {

View File

@ -2,7 +2,7 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
/** @type {import("../htmx").HtmxInternalApi} */

View File

@ -2,7 +2,7 @@
'use strict';
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
// Save a reference to the global object (window in the browser)
var _root = this;

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('path-params', {
onEvent: function(name, evt) {

2
dist/ext/preload.js vendored
View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
// This adds the "preload" extension to htmx. By default, this will
// preload the targets of any tags with `href` or `hx-get` attributes

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('rails-method', {
onEvent: function (name, evt) {

View File

@ -1,7 +1,7 @@
(function(){
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
function maybeRemoveMe(elt) {
var timing = elt.getAttribute("remove-me") || elt.getAttribute("data-remove-me");

View File

@ -2,7 +2,7 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
/** @type {import("../htmx").HtmxInternalApi} */

View File

@ -1,6 +1,6 @@
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
htmx.defineExtension('restored', {
onEvent : function(name, evt) {

2
dist/ext/sse.js vendored
View File

@ -9,7 +9,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
/** @type {import("../htmx").HtmxInternalApi} */

2
dist/ext/ws.js vendored
View File

@ -8,7 +8,7 @@ This extension adds support for WebSockets to htmx. See /www/extensions/ws.md f
if (htmx.version && !htmx.version.startsWith("1.")) {
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
". It is recommended that you move to the version of this extension found on https://htmx.org/extensions")
}
/** @type {import("../htmx").HtmxInternalApi} */

View File

@ -105,7 +105,7 @@ directly in HTML, using [attributes](@/reference.md#attributes), so you can buil
htmx is small ([~14k min.gz'd](https://unpkg.com/htmx.org/dist/)),
[dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json),
[extendable](https://extensions.htmx.org) & has **reduced** code base sizes by [67% when compared with react](@/essays/a-real-world-react-to-htmx-port.md)
[extendable](https://htmx.org/extensions) & has **reduced** code base sizes by [67% when compared with react](@/essays/a-real-world-react-to-htmx-port.md)
<h2>motivation</h2>

View File

@ -2,7 +2,7 @@
title = "Javascript API"
+++
While it is not a focus of the library, htmx does provide a small API of helper methods, intended mainly for [extension development](https://extensions.htmx.org) or for working with [events](@/events.md).
While it is not a focus of the library, htmx does provide a small API of helper methods, intended mainly for [extension development](https://htmx.org/extensions) or for working with [events](@/events.md).
The [hyperscript](https://hyperscript.org) project is intended to provide more extensive scripting support
for htmx-based applications.
@ -183,7 +183,7 @@ to provide custom WebSocket setup.
### Method - `htmx.defineExtension()` {#defineExtension}
Defines a new htmx [extension](https://extensions.htmx.org).
Defines a new htmx [extension](https://htmx.org/extensions).
##### Parameters

View File

@ -2,7 +2,7 @@
title = "hx-ext"
+++
The `hx-ext` attribute enables an htmx [extension](https://extensions.htmx.org) for an element and all its children.
The `hx-ext` attribute enables an htmx [extension](https://htmx.org/extensions) for an element and all its children.
The value can be a single extension name or a comma separated list of extensions to apply.

View File

@ -448,7 +448,7 @@ cost of more CPU.
The following extensions are available for morph-style swaps:
* [Idiomorph](https://github.com/bigskysoftware/idiomorph#htmx) - A morphing algorithm created by the htmx developers.
* [Idiomorph](/extensions/idiomorph) - A morphing algorithm created by the htmx developers.
* [Morphdom Swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) - Based on the [morphdom](https://github.com/patrick-steele-idem/morphdom),
the original DOM morphing library.
* [Alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) - Based on the [alpine morph](https://alpinejs.dev/plugins/morph) plugin, plays
@ -841,7 +841,7 @@ As such, the normal HTML accessibility recommendations apply. For example:
## Web Sockets & SSE {#websockets-and-sse}
Web Sockets and Server Sent Events (SSE) are supported via extensions. Please see
the [SSE extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/sse/README.md) and [WebSocket extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ws/README.md)
the [SSE extension](/extensions/sse) and [WebSocket extension](/extensions/ws)
pages to learn more.
## History Support {#history}
@ -999,7 +999,7 @@ If you wanted to swap everything, regardless of HTTP response code, you could us
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!--all responses are swapped-->
```
Finally, it is worth considering using the [Response Targets](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/response-targets/README.md)
Finally, it is worth considering using the [Response Targets](/extensions/reponse-targets)
extension, which allows you to configure the behavior of response codes declaratively via attributes.
### CORS
@ -1117,20 +1117,46 @@ Please see the [Animation Guide](@/examples/animations.md) for more details on t
## Extensions
Htmx has an extension mechanism that allows you to customize the libraries' behavior.
Extensions [are defined in javascript](https://github.com/bigskysoftware/htmx-extensions/tree/main?tab=readme-ov-file#defining-an-extension) and then used via
the [`hx-ext`](@/attributes/hx-ext.md) attribute:
htmx provides an [extensions](/extensions) mechanism that allows you to customize the libraries' behavior.
Extensions [are defined in javascript](/extensions/building) and then enabled via
the [`hx-ext`](@/attributes/hx-ext.md) attribute.
Here is how you would install the [idiomorph](@extension/idiomorph) extension, which allows you to use the
[Idiomorph](https://github.com/bigskysoftware/idiomorph) DOM morphing algorithms for htmx swaps:
```html
<div hx-ext="debug">
<button hx-post="/example">This button used the debug extension</button>
<button hx-post="/example" hx-ext="ignore:debug">This button does not</button>
</div>
<head>
<script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph-ext.min.js"></script>
</head>
<body hx-ext="morph">
...
<button hx-post="/example" hx-swap="morph" hx-target="#content">
Update Content
</button>
...
</body>
```
For existing extensions, please [see the extensions site](https://extensions.htmx.org).
First the extension is included (it should be included *after* `htmx.js` is included), then the extension is referenced
by name via the `hx-ext` attribute. This enables you to then use the `morph` swap.
If you are interested in adding your own extension to htmx, please [see the extension docs](https://github.com/bigskysoftware/htmx-extensions/tree/main?tab=readme-ov-file#defining-an-extension).
### Core Extensions
htmx supports a few "core" extensions, which are supported by the htmx development team:
* [head-support](/extensions/head-support) - support for merging head tag information (styles, etc.) in htmx requests |
* [htmx-1-compat](/extensions/htmx-1-compat) - restores htmx 1 defaults & functionality
* [idiomorph](/extensions/idiomorph) - supports the `morph` swap strategy using idiomorph
* [preload](/extensions/preload) - allows you to preload content for better performance
* [response-targets](/extensions/response-targets) - allows you to target elements based on HTTP response codes (e.g. `404`)
* [sse](/extensions/sse) - support for [Serve Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
* [ws](/extensions/ws) - support for [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
You can see all available extensions on the [Extensions](/extensions) page.
### Creating Extensions
If you are interested in adding your own extension to htmx, please [see the extension docs](/extensions/building).
## Events & Logging {#events}

View File

@ -1,3 +1,52 @@
+++
redirect_to = "https://extensions.htmx.org"
title = "Extensions"
+++
htmx supports extensions to augment the core hypermedia infrastructure it provides. The extension mechanism takes
pressure off the core library to add new features, allowing it to focus on its main purpose of
[generalizing hypermedia controls](https://dl.acm.org/doi/10.1145/3648188.3675127).
If you are interested in creating an extension for htmx, please see [Building htmx Extensions](/extensions/building).
htmx extensions are split into two categories:
* [core extensions](#core-extensions) - supported by the htmx team
* [community extensions](#community-extensions) - supported by the broader community
## Core Extensions
| Name | Description |
|--------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [head-support](/extensions/head-support) | Provides support for merging head tag information (styles, etc.) in htmx requests |
| [htmx-1-compat](/extensions/htmx-1-compat) | Rolls back most of the behavioral changes of htmx 2 to the htmx 1 defaults. |
| [idiomorph](/extensions/idiomorph) | Provides a `morph` swap strategy based on the [idiomorph](https://github.com/bigskysoftware/idiomorph/) morphing library, which was created by the htmx team. |
| [preload](/extensions/preload) | This extension allows you to load HTML fragments into your browser's cache before they are requested by the user, so that additional pages appear to users to load nearly instantaneously. |
| [response-targets](/extensions/response-targets) | This extension allows you to specify different target elements to be swapped when different HTTP response codes are received. |
| [sse](/extensions/sse) | Provides support for [Serve Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) directly from HTML. |
| [ws](/extensions/ws) | Provides bi-directional communication with [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications) servers directly from HTML |
## Community Extensions
| Name | Description |
|--------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [ajax-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/ajax-header/README.md) | Adds an `X-Requested-With` header to all requests made by htmx |
| [alpine-morph](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/alpine-morph/README.md) | Alpine.js now has a lightweight [morph plugin](https://alpinejs.dev/plugins/morph) and this extension allows you to use it as the swapping mechanism in htmx which is necessary to retain Alpine state when you have entire Alpine components swapped by htmx. |
| [class-tools](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/class-tools/README.md) | The `class-tools` extension allows you to specify CSS classes that will be swapped onto or off of the elements by using a `classes` or `data-classes` attribute. |
| [client-side-templates](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/client-side-templates/README.md) | This extension supports transforming a JSON/XML request response into HTML via a client-side template before it is swapped into the DOM. |
| [debug](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/debug/README.md) | This extension includes log all htmx events for the element it is on, either through the `console.debug` function or through the `console.log` function with a `DEBUG:` prefix. |
| [disable-element](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/disable-element/README.md) | This extension disables an element during an htmx request, when configured on the element triggering the request. Note that this functionality is now part of the core of htmx via the `hx-disabled-elt` attribute |
| [event-header](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/event-header/README.md) | This extension adds the `Triggering-Event` header to requests. The value of the header is a JSON serialized version of the event that triggered the request. |
| [include-vals](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/include-vals/README.md) | The `include-vals` extension allows you to programmatically include values in a request with a `include-vals` attribute. The value of this attribute is one or more name/value pairs, which will be evaluated as the fields in a javascript object literal. |
| [json-enc-custom](https://github.com/Emtyloc/json-enc-custom/blob/main/README.md) | This extension works similarly to json-enc but allows for very complex structures, such as embedding JSON objects, lists, or handling indexes, just by using the name attribute. |
| [json-enc](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) | This extension encodes parameters in JSON format instead of url format. |
| [loading-states](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/loading-states/README.md) | This extension allows you to easily manage loading states while a request is in flight, including disabling elements, and adding and removing CSS classes. |
| [morphdom-swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/morphdom-swap/README.md) | Provides a `morph` swap strategy based on the [morphdom](https://github.com/patrick-steele-idem/morphdom/) morphing library. |
| [multi-swap](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/multi-swap/README.md) | This extension allows you to swap multiple elements marked from the HTML response. You can also choose for each element which swap method should be used. |
| [no-cache](https://github.com/craigharman/htmx-ext-no-cache/README.md) | This extension forces HTMX to bypass client caches and make a new request. An `hx-no-cache` header is also added to allow server-side caching to be bypassed. |
| [path-deps](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md) | This extension supports expressing inter-element dependencies based on paths, inspired by the [intercooler.js dependencies mechanism](http://intercoolerjs.org/docs.html#dependencies). |
| [path-params](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md) | This extension uses request parameters to populate path variables. Used parameters are removed so they won't be sent in the query string or body anymore. |
| [remove-me](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/remove-me/README.md) | Allows you to remove an element after a specified interval. |
| [replace-params](https://github.com/fanelfaa/htmx-ext-replace-params/blob/main/README.md) | This extension uses request parameters to replace existing parameters. If given value is empty string then parameter will be deleted. This extension would be useful in situations like pagination, search that you only want to replace only few parameters instead of reset all parameters. |
| [restored](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/restored/README.md) | Triggers an event whenever a back button even is detected while using `hx-boost` |
| [safe-nonce](https://github.com/MichaelWest22/htmx-extensions/blob/main/src/safe-nonce/README.md) | The `safe-nonce` extension can be used to improve the security of the application/web-site and help avoid XSS issues by allowing you to return known trusted inline scripts safely |
| [signalr](https://github.com/Renerick/htmx-signalr/blob/master/README.md) | Provides bidirectional real-time communication via [SignalR](https://github.com/dotnet/AspNetCore/tree/main/src/SignalR). |

View File

@ -0,0 +1,31 @@
+++
title = "Building htmx Extensions"
+++
To define an extension you call the `htmx.defineExtension()` function:
```html
<script>
htmx.defineExtension('my-ext', {
onEvent : function(name, evt) {
console.log("Fired event: " + name, evt);
}
})
</script>
```
Typically, this is done in a stand-alone javascript file, rather than in an inline `script` tag.
Extensions should have names that are dash separated and that are reasonably short and descriptive.
Extensions can override the following default extension points to add or change functionality:
```javascript
{
onEvent : function(name, evt) {return true;},
transformResponse : function(text, xhr, elt) {return text;},
isInlineSwap : function(swapStyle) {return false;},
handleSwap : function(swapStyle, target, fragment, settleInfo) {return false;},
encodeParameters : function(xhr, parameters, elt) {return null;}
}
```

View File

@ -0,0 +1,113 @@
+++
title = "htmx Head Tag Support Extension"
+++
The `head-support` extension adds support for [head tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
in responses to htmx requests.
htmx began as a library focused on _partial replacement_ of HTML within the `body` tag. As such, merging additional
information such as the head tag was not a focus of the library. (This is in contrast with, for example, TurboLinks,
which was focused on merging entire web pages retrieved via AJAX into the browser.)
The [`hx-boost`](@/attributes/hx-boost.md) attribute moved htmx closer to this world of full HTML-document support &
support for extracting the `title` tag out of head elements was eventually added, but full head tag support has never been
a feature of the library. This extension addresses that shortcoming.
## Install
```html
<script src="https://unpkg.com/htmx-ext-head-support@2.0.1/head-support.js"></script>
```
## Usage
```html
<body hx-ext="head-support">
...
</body>
```
With this installed, all responses that htmx receives that contain a `head` tag in them (even if they are not complete
HTML documents with a root `<html>` element) will be processed.
How the head tag is handled depends on the type of htmx request.
If the htmx request is from a boosted element, then the following merge algorithm is used:
* Elements that exist in the current head as exact textual matches will be left in place
* Elements that do not exist in the current head will be added at the end of the head tag
* Elements that exist in the current head, but not in the new head will be removed from the head
If the htmx request is from a non-boosted element, then all content will be _appended_ to the existing head element.
If you wish to override this behavior in either case, you can place the `hx-head` attribute on the new `<head>` tag,
with either of the following two values:
* `merge` - follow the merging algorithm outlined above
* `append` - append the elements to the existing head
### Controlling Merge Behavior
Beyond this, you may also control merging behavior of individual elements with the following attributes:
* If you place `hx-head="re-eval"` on a head element, it will be re-added (removed and appended) to the head tag on every
request, even if it already exists. This can be useful to execute a script on every htmx request, for example.
* If you place `hx-preserve="true"` on an element, it will never be removed from the head
### Example
As an example, consider the following head tag in an existing document:
```html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<link rel="stylesheet" href="/css/site1.css">
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
</head>
```
If htmx receives a request containing this new head tag:
```html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<link rel="stylesheet" href="/css/site2.css">
<script src="/js/script2.js"></script>
<script src="/js/script3.js"></script>
</head>
```
Then the following operations will occur:
* `<link rel="stylesheet" href="https://the.missing.style">` will be left alone
* `<link rel="stylesheet" href="/css/site1.css">` will be removed from the head
* `<link rel="stylesheet" href="/css/site2.css">` will be added to the head
* `<script src="/js/script1.js"></script>` will be removed from the head
* `<script src="/js/script2.js"></script>` will be left alone
* `<script src="/js/script3.js"></script>` will be added to the head
The final head element will look like this:
```html
<head>
<link rel="stylesheet" href="https://the.missing.style">
<script src="/js/script2.js"></script>
<link rel="stylesheet" href="/css/site2.css">
<script src="/js/script3.js"></script>
</head>
```
## Events
This extension triggers the following events:
* `htmx:removingHeadElement` - triggered when a head element is about to be removed, with the element being removed
available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be removed.
* `htmx:addingHeadElement` - triggered when a head element is about to be added, with the element being added
available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be added.
* `htmx:afterHeadMerge` - triggered after a head tag merge has occurred, with the following values available in the event `detail`:
* `added` - added head elements
* `kept` - kept head elements
* `removed` - removed head elements
* `htmx:beforeHeadMerge` - triggered before a head merge occurs

View File

@ -0,0 +1,41 @@
+++
title = "htmx 1.x Compatability Extension"
+++
The `htmx-1-compat` extension allows you to almost seamlessly upgrade from htmx 1.x to htmx 2.
## Install
```html
<script src="https://unpkg.com/htmx-ext-htmx-1-compat@2.0.0/htmx-1-compat.js"></script>
```
## What it covers
Htmx 2 introduced a few [breaking changes](https://v2-0v2-0.htmx.org/migration-guide-htmx-1/).
To make upgrading from htmx 1.x to htmx 2 easier, we're providing this extension that reverts most of those, so you're
able to benefit from the other changes without breaking your application.
### Obsolete attributes
- htmx 2 removed the deprecated [hx-ws](https://htmx.org/attributes/hx-ws/)
and [hx-sse](https://htmx.org/attributes/hx-sse/) attributes, that this extension restores.
- htmx 2 removed the deprecated `hx-on` attribute in favor of the
wildcard [`hx-on*` attribute](https://htmx.org/attributes/hx-on/), that this extension restores.
### Default Changes
- reverts [htmx.config.scrollBehavior](https://htmx.org/reference/#config) to 'smooth'.
- makes `DELETE` requests use a form-encoded body rather than URL parameters (htmx 2 uses URL parameters for `DELETE` as
default as per [the spec](https://www.rfc-editor.org/rfc/rfc9110.html#name-delete)).
- allows cross-domain requests by default (htmx 2 now forbids it by default).
## What it does not cover
- IE11 support was dropped in htmx 2, and this extension cannot revert that. If you need IE11 support, please stay with
htmx 1 that will continue being supported.
- htmx 2 introduced the breaking change that is the [swap method](https://v2-0v2-0.htmx.org/api/#swap) to the extensions
API. If you were only using core extensions, then you shouldn't need any additional work. If you were using custom or
community extensions, make sure that they were updated to work with htmx 2's API.

View File

@ -0,0 +1,44 @@
+++
title = "htmx Idiomorph Extension"
+++
[Idiomorph](https://github.com/bigskysoftware/idiomorph) is a DOM morphing algorithm created by the htmx creator. DOM
morphing is a process where an existing DOM tree is "morphed" into the shape of another in a way that resuses as much of
the existing DOM's nodes as possible. By preserving nodes when changing from one tree to another you can present a
much smoother transition between the two states.
You can use the idiomorph morphing algorith as a [swapping](@attributes/hx-swap) strategy by including the idiomorph
extension.
## Install
```html
<script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph-ext.min.js"></script>
```
## Usage
Once you have referenced the idiomorph extension, you can register it with the name `morph` on the body and then being
using `morph`, `morph:outerHTML` or `morph:innerHTML` as swap strategies.
* `morph` & `morph:outerHTML` will morph the target element as well as it's children
* `morph:innerHTML` will morph only the inner children of an element, leaving the target untouched
```html
<body hx-ext="morph">
<button hx-get="/example" hx-swap="morph">
Morph My Outer HTML
</button>
<button hx-get="/example" hx-swap="morph:outerHTML">
Morph My Outer HTML
</button>
<button hx-get="/example" hx-swap="morph:innerHTML">
Morph My Inner HTML
</button>
</body>
```

View File

@ -0,0 +1,139 @@
+++
title = "htmx Preload Extension"
+++
The `preload` extension allows you to load HTML fragments into your browser's cache before they are requested by the
user, so that additional pages appear to users to load nearly instantaneously. As a developer, you can customize its
behavior to fit your applications needs and use cases.
**IMPORTANT:** Preloading content judiciously can improve your web application's perceived performance, but preloading
too many resources can negatively impact your visitors' bandwidth and your server performance by initiating too many
unused requests. Use this extension carefully!
## Install
```html
<script src="https://unpkg.com/htmx-ext-preload@2.0.1/preload.js"></script>
```
## Usage
Register the extension with htmx using the `hx-ext` attribute. Then, add a `preload` attribute to any hyperlinks
and `hx-get` elements you want to preload. By default, resources will be loaded as soon as the `mousedown` event begins,
giving your application a roughly 100-200ms head start on serving responses. See configuration below for other options.
```html
<body hx-ext="preload">
<h1>What Works</h2>
<a href="/server/1" preload>WILL BE requested using a standard XMLHttpRequest() and default options (below)</a>
<button hx-get="/server/2" preload>WILL BE requested with additional htmx headers.</button>
<h1>What WILL NOT WORK</h1>
<a href="/server/3">WILL NOT be preloaded because it does not have an explicit "preload" attribute</a>
<a hx-post="/server/4" preload>WILL NOT be preloaded because it is an HX-POST transaction.</a>
</body>
```
### Inheriting Preload Settings
You can add the `preload` attribute to the top-level element that contains several `<a href="">` or `hx-get=""`
elements, and all of them will be preloaded. Be careful with this setting, because you can end up wasting bandwidth if
you preload many more resources than you need.
```html
<body hx-ext="preload">
<ul preload>
<li><a href="/server/1">This will be preloaded because of the attribute in the node above.</a>
<li><a href="/server/2">This will also be preloaded for the same reason.</a>
<li><a href="/server/3">This will be preloaded, too. Lorem ipsum.</a>
</ul>
</body>
```
### Preloading of Linked Images
After an HTML page (or page fragment) is preloaded, this extension can also preload linked image resources. It will not
load or run linked Javascript or Cascading Stylesheet content, whether linked or embedded in the preloaded HTML. To
preload images as well, use the following syntax.
```html
<div hx-ext="preload">
<a href="/my-next-page" preload="mouseover" preload-images="true">Next Page</a>
</div>
```
### Configuration
Defaults for this extension are chosen to balance users' perceived performance with potential load on your servers from
unused requests. As a developer, you can modify two settings to customize this behavior to your specific use cases.
#### preload="mousedown" (DEFAULT)
The default behavior for this extension is to begin loading a resource when the user presses the mouse down. This is a
conservative setting that guarantees the user actually intends to use the linked resource. Because user click events
typically take 100-200ms to complete, this setting gives your server a significant headstart compared with a regular
click.
```html
<a href="/server/1" preload="mousedown">This will be preloaded when the user begins to click.</a>
```
#### preload="mouseover"
To preload links more aggressively, you can trigger the preload to happen when the user's mouse hovers over the link
instead. To prevent many resources from being loaded when the user scrolls or moves the mouse across a large list of
objects, a 100ms delay is built in to this action. If the user's mouse leaves the element *before* this timeout expires,
then the resource is not preloaded.
Typical users hover over links for several hundred milliseconds before they click, which gives your server even more
time to respond to the request than the `mousedown` option
above. [Test your own hover timing here.](http://instantclick.io/click-test). However, be careful when using this
option because it can increase server load by requesting resources unnecessarily.
```html
<a href="/server/1" preload="mouseover">This will be preloaded when the user's mouse remains over it for more than
100ms.</a>
```
#### preload="custom-event-name"
Preload can also listen to any custom event within the system, triggering resources to be preloaded (if they have not
already been cached by the browser). The extension itself generates an event called `preload:init` that can be used to
trigger preloads as soon as an object has been processed by htmx.
```html
<body hx-ext="preload">
<button hx-get="/server" preload="preload:init" hx-target="idLoadMore">Load More</a>
<div id="idLoadMore">
Content for this DIV will be preloaded as soon as the page is ready.
Clicking the button above will swap it into the DOM.
</div>
</body>
```
### About Touch Events
To accommodate touchscreen devices, an additional `ontouchstart` event handler is added whenever you specify
a `mouseover` or `mousedown` trigger. This extra trigger fires immediately (no waiting period) whenever the user touches
the screen, saving you 300ms of waiting time on Android, and 450ms on iOS.
### Limitations
* Links must be marked with a `preload` attribute, or have an ancestor node that has the `preload` attribute.
* Only `GET` transactions (including `<a href="">` and `hx-get=""`) can be preloaded. Following REST principles, `GET`
transactions are assumed to not make any significant changes to a resource. Transactions that can potentially make a
change (such as `POST`, `PUT`, and `DELETE`) will not be preloaded under any circumstances.
* When listening to `mouseover` events, preload waits for 100ms before downloading the linked resource. If the mouse
leaves the resource before this timeout expires, the resource is not preloaded.
* Preloaded responses will only be cached in the browser if the response headers allow it. For example, the response
header `Cache-Control: private, max-age=60` allows the browser to cache the response,
whereas `Cache-Control: no-cache` prevents it.
## Credits
The behavior for this plugin was inspired by the work done by [Alexandre Dieulot](https://github.com/dieulot)
on [InstantClick](http://instantclick.io/), which is released under the MIT license.

View File

@ -0,0 +1,142 @@
+++
title = "htmx Response Targets Extension"
+++
This extension allows you to specify different target elements to be swapped when
different HTTP response codes are received.
It uses attribute names in a form of ``hx-target-[CODE]`` where `[CODE]` is a numeric
HTTP response code with the optional wildcard character at its end. You can also use
`hx-target-error`, which handles both 4xx and 5xx response codes.
The value of each attribute can be:
* A CSS query selector of the element to target.
* `this` which indicates that the element that the `hx-target` attribute is on is the target.
* `closest <CSS selector>` which will find the closest parent ancestor that matches the given CSS selector
(e.g. `closest tr` will target the closest table row to the element).
* `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector.
* `next <CSS selector>` which will scan the DOM forward for the first element that matches the given CSS selector.
(e.g. `next .error` will target the closest following sibling element with `error` class)
* `previous <CSS selector>` which will scan the DOM backwards for the first element that matches the given CSS selector.
(e.g `previous .error` will target the closest previous sibling with `error` class)
## Install
```html
<script src="https://unpkg.com/htmx-ext-response-targets@2.0.0/response-targets.js"></script>
```
## Configure (optional)
* When `HX-Retarget` response header is received it disables any lookup that would be
performed by this extension but any responses with error status codes will be
swapped (normally they would not be, even with target set via header) and internal
error flag (`isError`) will be modified. You may change this and choose to ignore
`HX-Retarget` header when `hx-target-…` is in place by setting a configuration flag
`htmx.config.responseTargetPrefersRetargetHeader` to `false` (default is
`true`). Note that this extension only performs a simple check whether the header
is set and target exists. It is not extracting target's value from the header but
trusts it was set by HTMX core logic.
* Normally, any target which is already established by HTMX built-in functions or
extensions called before will be overwritten if a matching `hx-target-…` tag is
found. You may change it by using a configuration flag
`htmx.config.responseTargetPrefersExisting` to `true` (default is `false`). This is
kinky and risky option. It has a real-life applications similar to a skilled,
full-stack tardigrade eating parentheses when no one is watching.
* `isError` flag on the `detail` member of an event associated with swapping the
content with `hx-target-[CODE]` will be set to `false` when error response code is
received. This is different from the default behavior. You may change this by
setting a configuration flag `htmx.config.responseTargetUnsetsError` to `false`
(default is `true`).
* `isError` flag on the `detail` member of an event associated with swapping the
content with `hx-target-[CODE]` will be set to `false` when non-erroneous response
code is received. This is no different from the default behavior. You may change
this by setting a configuration flag `htmx.config.responseTargetSetsError` to
`true` (default is `false`). This setting will not affect the response code 200
since it is not handled by this extension.
## Usage
Here is an example that targets a `div` for normal (200) response but another `div`
for 404 (not found) response, and yet another for all 5xx response codes:
```html
<div hx-ext="response-targets">
<div id="response-div"></div>
<button hx-post="/register"
hx-target="#response-div"
hx-target-5*="#serious-errors"
hx-target-404="#not-found">
Register!
</button>
<div id="serious-errors"></div>
<div id="not-found"></div>
</div>
```
* The response from the `/register` URL will replace contents of the `div` with the
`id` `response-div` when response code is 200 (OK).
* The response from the `/register` URL will replace contents of the `div` with the `id`
`serious-errors` when response code begins with a digit 5 (server errors).
* The response from the `/register` URL will replace contents of the `div` with
the `id` `not-found` when response code is 404 (Not Found).
Sometimes you may not want to handle 5xx and 4xx errors separately, in which case you
can use `hx-target-error`:
```html
<div hx-ext="response-targets">
<div id="response-div"></div>
<button hx-post="/register"
hx-target="#response-div"
hx-target-error="#any-errors">
Register!
</button>
<div id="any-errors"></div>
</div>
```
2xx codes will be handled as in the previous example. However, when the response code is 5xx
or 4xx, the response from `/register` will replace the contents of the `div` with the `id`
`any-errors`.
## Wildcard resolution
When status response code does not match existing `hx-target-[CODE]` attribute name
then its numeric part expressed as a string is trimmed with last character being
replaced with the asterisk (`*`). This lookup process continues until the attribute
is found or there are no more digits.
For example, if a browser receives 404 error code, the following attribute names will
be looked up (in the given order):
* `hx-target-404`
* `hx-target-40*`
* `hx-target-4*`
* `hx-target-*`.
_If you are using tools that do not support asterisks in HTML attributes, you
may instead use the `x` character, e.g., `hx-target-4xx`._
## Notes
* `hx-target-…` is inherited and can be placed on a parent element.
* `hx-target-…` cannot be used to handle HTTP response code 200.
* `hx-target-…` will honor `HX-Retarget` by default and will prefer it over any
calculated target but it can be changed by disabling the
`htmx.config.responseTargetPrefersRetargetHeader` configuration option.
* To avoid surprises the `hx-ext` attribute used to enable this extension should be
placed on a parent element containing elements with `hx-target-…` and `hx-target`
attributes.
## See also
* [`hx-target`](https://htmx.org/reference/hx-target.md), specifies the target element to be swapped

View File

@ -0,0 +1,234 @@
+++
title = "htmx Server Sent Event (SSE) Extension"
+++
The `Server Sent Events` extension connects to
an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) directly
from HTML. It manages the connections to your web server, listens for server events, and then swaps their contents into
your htmx webpage in real-time.
SSE is a lightweight alternative to WebSockets that works over existing HTTP connections, so it is easy to use through
proxy servers and firewalls. Remember, SSE is a uni-directional service, so you cannot send any messages to an SSE
server once the connection has been established. If you need bi-directional communication, then you should consider
using [WebSockets](@web-sockets.md) instead.
This extension replaces the experimental `hx-sse` attribute built into previous versions of htmx. For help migrating
from older versions, see the migration guide at the bottom of this page.
Use the following attributes to configure how SSE connections behave:
* `sse-connect="<url>"` - The URL of the SSE server.
* `sse-swap="<message-name>"` - The name of the message to swap into the DOM.
* `hx-trigger="sse:<message-name>"` - SSE messages can also trigger HTTP callbacks using
the [`hx-trigger`](https://htmx.org/reference/hx-trigger.md) attribute.
* `sse-close=<message-name>` - To close the EventStream gracefully when that message is received. This might be helpful
if you want to send information to a client that will eventually stop.
## Install
```html
<script src="https://unpkg.com/htmx-ext-sse@2.2.2/sse.js"></script>
```
## Usage
```html
<div hx-ext="sse" sse-connect="/chatroom" sse-swap="message">
Contents of this box will be updated in real time
with every SSE message received from the chatroom.
</div>
```
### Connecting to an SSE Server
To connect to an SSE server, use the `hx-ext="sse"` attribute to install the extension on that HTML element, then
add `sse-connect="<url>"` to the element to make the connection.
When designing your server application, remember that SSE works just like any HTTP request. Although you cannot send any
messages to the server after you have established a connection, you can send parameters to the server along with your
request. So, instead of making an SSE connection to your server at `https://my-server/chat-updates` you can also connect
to `https://my-server/chat-updates?friends=true&format=detailed`. This allows your server to customize its responses to
what your client needs.
### Receiving Named Events
SSE messages consist of an event name and a data packet. No other metadata is allowed in the message. Here is an
example:
```txt
event: EventName
data: <div>Content to swap into your HTML page.</div>
```
We'll use the `sse-swap` attribute to listen for this event and swap its contents into our webpage.
```html
<div hx-ext="sse" sse-connect="/event-source" sse-swap="EventName"></div>
```
Notice that the name `EventName` from the server's message must match the value in the `sse-swap` attribute. Your server
can use as many different event names as necessary, but be careful: browsers can only listen for events that have been
explicitly named. So, if your server sends an event named `ChatroomUpdate` but your browser is only listening for events
named `ChatUpdate` then the extra event will be discarded.
### Receiving Unnamed Events
SSE messages can also be sent without any event name. In this case, the browser uses the default name `message` in its
place. The same rules specified above still apply. If your server sends an unnamed message, then you must listen for it
by including `sse-swap="message"`. There is no option for using a catch-all name. Here's how this looks:
```txt
data: <div>Content to swap into your HTML page.</div>
```
```html
<div hx-ext="sse" sse-connect="/event-source" sse-swap="message"></div>
```
### Receiving Multiple Events
You can also listen to multiple events (named or unnamed) from a single EventSource. Listeners must be either 1) the
same element that contains the `hx-ext` and `sse-connect` attributes, or 2) child elements of the element containing
the `hx-ext` and `sse-connect` attributes.
```html
Multiple events in the same element
<div hx-ext="sse" sse-connect="/server-url" sse-swap="event1,event2"></div>
Multiple events in different elements (from the same source).
<div hx-ext="sse" sse-connect="/server-url">
<div sse-swap="event1"></div>
<div sse-swap="event2"></div>
</div>
```
### Trigger Server Callbacks
When a connection for server sent events has been established, child elements can listen for these events by using the
special [`hx-trigger`](https://htmx.org/reference/hx-trigger.md) syntax `sse:<event_name>`. This, when combined with
an `hx-get` or similar will trigger the element to make a request.
Here is an example:
```html
<div hx-ext="sse" sse-connect="/event_stream">
<div hx-get="/chatroom" hx-trigger="sse:chatter">
...
</div>
</div>
```
This example establishes an SSE connection to the `event_stream` end point which then triggers
a `GET` to the `/chatroom` url whenever the `chatter` event is seen.
### Automatic Reconnection
If the SSE Event Stream is closed unexpectedly, browsers are supposed to attempt to reconnect automatically. However, in
rare situations this does not work and your browser can be left hanging. This extension adds its own reconnection
logic (using an [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff)) on top of the
browser's automatic reconnection, so that your SSE streams will always be as reliable as possible.
### Testing SSE Connections with the Demo Server
Htmx includes a demo SSE server written in Node.js that will help you to see SSE in action, and begin bootstrapping your
own SSE code. It is located in the /test/ws-sse folder of the htmx distribution. Look at /test/ws-sse/README.md for
instructions on running and using the test server.
### Migrating from Previous Versions
Previous versions of htmx used a built-in tag `hx-sse` to implement Server Sent Events. This code has been migrated into
an extension instead. Here are the steps you need to take to migrate to this version:
| Old Attribute | New Attribute | Comments |
|--------------------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `hx-sse=""` | `hx-ext="sse"` | Use the `hx-ext="sse"` attribute to install the SSE extension into any HTML element. |
| `hx-sse="connect:<url>"` | `sse-connect="<url>"` | Add a new attribute `sse-connect` to the tag that specifies the URL of the Event Stream. This attribute must be in the same tag as the `hx-ext` attribute. |
| `hx-sse="swap:<EventName>"` | `sse-swap="<EventName>"` | Add a new attribute `sse-swap` to any elements that will be swapped in via the SSE extension. This attribute must be placed **on** or **inside of** the tag containing the `hx-ext` attribute. |
| `hx-trigger="sse:<EventName>"` | NO CHANGE | any `hx-trigger` attributes do not need to change. The extension will identify these attributes and add listeners for any events prefixed with `sse:` |
### Listening to events dispatched by this extension
This extension dispatches several events. You can listen for these events like so:
```javascript
document.body.addEventListener('htmx:sseBeforeMessage', function (e) {
// do something before the event data is swapped in
})
```
Each event object has a `detail` field that contains details of the event.
#### `htmx:sseOpen`
This event is dispatched when an SSE connection has been successfully established.
##### Details
* `detail.elt` - The element on which the SSE connection was setup. This is the element which has the `sse-connect`
attribute.
* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) object.
#### `htmx:sseError`
This event is dispatched when an SSE connection could not be established.
##### Details
* `detail.error` - The error that occured while creating
an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
#### `htmx:sseBeforeMessage`
This event is dispatched just before the SSE event data is swapped into the DOM. If you don't want to swap
call `preventDefault()` on the event. Additionally the `detail` field is
a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created
by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.
##### Details
* `detail.elt` - The swap target.
#### `htmx:sseMessage`
This event is dispatched after the SSE event data has been swapped into the DOM. The `detail` field is
a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created
by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.
#### `htmx:sseClose`
This event is dispatched in three different closing scenario. To control for the scenario the user can control for the
evt.detail.sseclose property.
```javascript
document.body.addEventListener('htmx:sseClose', function (e) {
const reason = e.detail.type
switch (reason) {
case "nodeMissing":
// Parent node is missing and therefore connection was closed
...
case "nodeReplaced":
// Parent node replacement caused closing of connection
...
case "message":
// connection was closed due to reception of message sse-close
...
}
})
```
##### Details
* `detail.elt` - The swap target.
### Additional SSE Resources
* [Wikipedia](https://en.wikipedia.org/wiki/Server-sent_events)
* [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
* [Can I Use?](https://caniuse.com/eventsource)

View File

@ -0,0 +1,256 @@
+++
title = "htmx Web Socket extension"
+++
The Web Sockets extension enables easy, bi-directional communication
with [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)
servers directly from HTML. This replaces the experimental `hx-ws` attribute built into previous versions of htmx. For
help migrating from older versions, see the [Migrating](#migrating-from-previous-versions) guide at the bottom of this
page.
Use the following attributes to configure how WebSockets behave:
* `ws-connect="<url>"` or `ws-connect="<prefix>:<url>"` - A URL to establish a `WebSocket` connection against.
* Prefixes `ws` or `wss` can optionally be specified. If not specified, HTMX defaults to adding the location's
scheme-type,
host and port to have browsers send cookies via websockets.
* `ws-send` - Sends a message to the nearest websocket based on the trigger value for the element (either the natural
event
or the event specified by [`hx-trigger`])
## Install
```html
<script src="https://unpkg.com/htmx-ext-ws@2.0.1/ws.js"></script>
```
## Usage
```html
<div hx-ext="ws" ws-connect="/chatroom">
<div id="notifications"></div>
<div id="chat_room">
...
</div>
<form id="form" ws-send>
<input name="chat_message">
</form>
</div>
```
### Configuration
WebSockets extension support two configuration options:
- `createWebSocket` - a factory function that can be used to create a custom WebSocket instances. Must be a function,
returning `WebSocket` object
- `wsBinaryType` - a string value, that defines
socket's [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType) property. Default value
is `blob`
### Receiving Messages from a WebSocket
The example above establishes a WebSocket to the `/chatroom` end point. Content that is sent down from the websocket
will
be parsed as HTML and swapped in by the `id` property, using the same logic
as [Out of Band Swaps](https://htmx.org/attributes/hx-swap-oob/).
As such, if you want to change the swapping method (e.g., append content at the end of an element or delegate swapping
to an extension),
you need to specify that in the message body, sent by the server.
```html
<!-- will be interpreted as hx-swap-oob="true" by default -->
<form id="form">
...
</form>
<!-- will be appended to #notifications div -->
<div id="notifications" hx-swap-oob="beforeend">
New message received
</div>
<!-- will be swapped using an extension -->
<div id="chat_room" hx-swap-oob="morphdom">
....
</div>
```
### Sending Messages to a WebSocket
In the example above, the form uses the `ws-send` attribute to indicate that when it is submitted, the form values
should be **serialized as JSON**
and send to the nearest enclosing `WebSocket`, in this case the `/chatroom` endpoint.
The serialized values will include a field, `HEADERS`, that includes the headers normally submitted with an htmx
request.
### Automatic Reconnection
If the WebSocket is closed unexpectedly, due to `Abnormal Closure`, `Service Restart` or `Try Again Later`, this
extension will attempt to reconnect until the connection is reestablished.
By default, the extension uses a
full-jitter [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) that chooses a randomized
retry delay that grows exponentially over time. You can use a different algorithm by writing it
into `htmx.config.wsReconnectDelay`. This function takes a single parameter, the number of retries, and returns the
time (in milliseconds) to wait before trying again.
```javascript
// example reconnect delay that you shouldn't use because
// it's not as good as the algorithm that's already in place
htmx.config.wsReconnectDelay = function (retryCount) {
return retryCount * 1000 // return value in milliseconds
}
```
The extension also implements a simple queuing mechanism that keeps messages in memory when the socket is not in `OPEN`
state and sends them once the connection is restored.
### Events
WebSockets extensions exposes a set of events that allow you to observe and customize its behavior.
#### Event - `htmx:wsConnecting` {#htmx:wsConnecting}
This event is triggered when a connection to a WebSocket endpoint is being attempted.
##### Details
* `detail.event.type` - the type of the event (`'connecting'`)
#### Event - `htmx:wsOpen` {#htmx:wsOpen}
This event is triggered when a connection to a WebSocket endpoint has been established.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.event` - the original event from the socket
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsClose` {#htmx:wsClose}
This event is triggered when a connection to a WebSocket endpoint has been closed normally.
You can check if the event was caused by an error by inspecting `detail.event` property.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.event` - the original event from the socket
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsError` {#htmx:wsError}
This event is triggered when `onerror` event on a socket is raised.
##### Details
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.error` - the error object
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsBeforeMessage` {#htmx:wsBeforeMessage}
This event is triggered when a message has just been received by a socket, similar to `htmx:beforeOnLoad`. This event
fires
before any processing occurs.
If the event is cancelled, no further processing will occur.
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.message` - raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsAfterMessage` {#htmx:wsAfterMessage}
This event is triggered when a message has been completely processed by htmx and all changes have been
settled, similar to `htmx:afterOnLoad`.
Cancelling this event has no effect.
* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute)
* `detail.message` - raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsConfigSend` {#htmx:wsConfigSend}
This event is triggered when preparing to send a message from `ws-send` element.
Similarly to [`htmx:configRequest`](https://htmx.org/events#htmx:configRequest), it allows you to modify the message
before sending.
If the event is cancelled, no further processing will occur and no messages will be sent.
##### Details
* `detail.parameters` - the parameters that will be submitted in the request
* `detail.unfilteredParameters` - the parameters that were found before filtering
by [`hx-select`](https://htmx.org/reference/hx-select.md)
* `detail.headers` - the request headers. Will be attached to the body in `HEADERS` property, if not falsy
* `detail.errors` - validation errors. Will prevent sending and
trigger [`htmx:validation:halted`](https://htmx.org/events#htmx:validation:halted) event if not empty
* `detail.triggeringEvent` - the event that triggered sending
* `detail.messageBody` - raw message body that will be sent to the socket. Undefined, can be set to value of any type,
supported by WebSockets. If set, will override
default JSON serialization. Useful, if you want to use some other format, like XML or MessagePack
* `detail.elt` - the element that dispatched the sending (the one with `ws-send` attribute)
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsBeforeSend` {#htmx:wsBeforeSend}
This event is triggered just before sending a message. This includes messages from the queue.
Message can not be modified at this point.
If the event is cancelled, the message will be discarded from the queue and not sent.
##### Details
* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute)
* `detail.message` - the raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Event - `htmx:wsAfterSend` {#htmx:wsAfterSend}
This event is triggered just after sending a message. This includes messages from the queue.
Cancelling the event has no effect.
##### Details
* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute)
* `detail.message` - the raw message content
* `detail.socketWrapper` - the wrapper around socket object
#### Socket wrapper
You may notice that all events expose `detail.socketWrapper` property. This wrapper holds the socket
object itself and the message queue. It also encapsulates reconnection algorithm. It exposes a few members:
- `send(message, fromElt)` - sends a message safely. If the socket is not open, the message will be persisted in the
queue
instead and sent when the socket is ready.
- `sendImmediately(message, fromElt)` - attempts to send a message regardless of socket state, bypassing the queue. May
fail
- `queue` - an array of messages, awaiting in the queue.
This wrapper can be used in your event handlers to monitor and manipulate the queue (e.g., you can reset the queue when
reconnecting), and to send additional messages (e.g., if you want to send data in batches).
The `fromElt` parameter is optional and, when specified, will trigger corresponding websocket events from
specified element, namely `htmx:wsBeforeSend` and `htmx:wsAfterSend` events when sending your messages.
### Testing with the Demo Server
Htmx includes a demo WebSockets server written in Node.js that will help you to see WebSockets in action, and begin
bootstrapping your own WebSockets code. It is located in the /test/ws-sse folder of the htmx distribution. Look at
/test/ws-sse/README.md for instructions on running and using the test server.
### Migrating from Previous Versions
Previous versions of htmx used a built-in tag `hx-ws` to implement WebSockets. This code has been migrated into an
extension instead. Here are the steps you need to take to migrate to this version:
| Old Attribute | New Attribute | Comments |
|-------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------|
| `hx-ws=""` | `hx-ext="ws"` | Use the `hx-ext="ws"` attribute to install the WebSockets extension into any HTML element. |
| `hx-ws="connect:<url>"` | `ws-connect="<url>"` | Add a new attribute `ws-connect` to the tag that defines the extension to specify the URL of the WebSockets server you're using. |
| `hx-ws="send"` | `ws-send=""` | Add a new attribute `ws-send` to mark any child forms that should send data to your WebSocket server |

View File

@ -11,8 +11,8 @@ We place a very high value on backwards compatibility, so in most cases this mig
* AMD Modules: `/dist/htmx.amd.js`
* CJS Modules: `/dist/htmx.cjs.js`
* The `/dist/htmx.js` file continues to be browser-loadable
* All extensions have been removed from the core htmx distribution and are distributed separately on
[their own website](https://extensions.htmx.org). While many 1.x extensions will continue to work with htmx 2, you
* All extensions have been removed from the core htmx distribution and are distributed separately. While many 1.x
extensions will continue to work with htmx 2, you
must upgrade the SSE extension to the 2.x version, and it is recommended that you upgrade all of them to the 2.x
versions.
* If you are still using the legacy `hx-ws` and `hx-sse` attributes, please upgrade to the extension versions

View File

@ -18,7 +18,7 @@ It is worth noting the difference in approach between what Intercooler set out t
This capability is augmented in primarily 2 ways:
1. [Extensions](https://extensions.htmx.org#reference). The htmx extension framework allows for custom extensions/plugins to achieve specific functionality. An example of this is the dependencies mechanism baked into Intercooler, which is not present in htmx core. but available via [an extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md). There are also other extensions which enables new behavior that Intercooler was not capable of out the box, e.g. the [`preload` extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/preload/README.md)
1. [Extensions](https://htmx.org/extensions). The htmx extension framework allows for custom extensions/plugins to achieve specific functionality. An example of this is the dependencies mechanism baked into Intercooler, which is not present in htmx core. but available via [an extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md). There are also other extensions which enables new behavior that Intercooler was not capable of out the box, e.g. the [`preload` extension](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/preload/README.md)
2. Using the htmx events system with vanilla javascript, [alpine.js](https://github.com/alpinejs/alpine/) or [hyperscript](https://hyperscript.org). Hyperscript is a small, open scripting language designed to be embedded in HTML, inspired by HyperTalk and is a companion project of htmx.

View File

@ -10,7 +10,7 @@ title = "Reference"
* [htmx Request Headers](#request_headers)
* [htmx Response Headers](#response_headers)
* [htmx Events](#events)
* [htmx Extensions](https://extensions.htmx.org)
* [htmx Extensions](/extensions)
* [JavaScript API](#api)
* [Configuration Options](#config)
@ -188,7 +188,7 @@ All other attributes available in htmx.
| [`htmx.config`](@/api.md#config) | A property that holds the current htmx config object
| [`htmx.createEventSource`](@/api.md#createEventSource) | A property holding the function to create SSE EventSource objects for htmx
| [`htmx.createWebSocket`](@/api.md#createWebSocket) | A property holding the function to create WebSocket objects for htmx
| [`htmx.defineExtension()`](@/api.md#defineExtension) | Defines an htmx [extension](https://extensions.htmx.org)
| [`htmx.defineExtension()`](@/api.md#defineExtension) | Defines an htmx [extension](https://htmx.org/extensions)
| [`htmx.find()`](@/api.md#find) | Finds a single element matching the selector
| [`htmx.findAll()` `htmx.findAll(elt, selector)`](@/api.md#find) | Finds all elements matching a given selector
| [`htmx.logAll()`](@/api.md#logAll) | Installs a logger that will log all htmx events
@ -200,7 +200,7 @@ All other attributes available in htmx.
| [`htmx.process()`](@/api.md#process) | Processes the given element and its children, hooking up any htmx behavior
| [`htmx.remove()`](@/api.md#remove) | Removes the given element
| [`htmx.removeClass()`](@/api.md#removeClass) | Removes a class from the given element
| [`htmx.removeExtension()`](@/api.md#removeExtension) | Removes an htmx [extension](https://extensions.htmx.org)
| [`htmx.removeExtension()`](@/api.md#removeExtension) | Removes an htmx [extension](https://htmx.org/extensions)
| [`htmx.swap()`](@/api.md#swap) | Performs swapping (and settling) of HTML content
| [`htmx.takeClass()`](@/api.md#takeClass) | Takes a class from other elements for the given element
| [`htmx.toggleClass()`](@/api.md#toggleClass) | Toggles a class from the given element

View File

@ -18,9 +18,7 @@
{% else -%}
{% set show_title = true -%}
{% endif -%}
{% if page.path is starting_with("/extensions/") -%}
{% set page_title = "The <code>" ~ page.title ~ "</code> Extension" -%}
{% elif page.path is starting_with("/attributes") -%}
{% if page.path is starting_with("/attributes") -%}
{% set page_title = "<code>" ~ page.title ~ "</code>" -%}
{% else -%}
{% set page_title = page.title -%}