diff --git a/CHANGELOG.md b/CHANGELOG.md index fb74dfd6..66c79a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,7 +85,7 @@ * Removed extensions and moved to their own repos linked off of * The website now supports dark mode! (Thanks [@pokonski](https://github.com/pokonski)!) -* The older, deprecated [SSE & WS](https://v1.htmx.org/docs/#websockets-and-sse) attributes were removed +* The older, deprecated [SSE & WS](https://v1.htmx.org/docs/) attributes were removed TODO fix link * Better support for [Web Components & Shadow DOM](https://htmx.org/examples/web-components/) * HTTP `DELETE` requests now use parameters, rather than form encoded bodies, for their payload (This is in accordance w/ the spec.) * Module support was split into different files: diff --git a/dev/four/ACTIVE.md b/dev/four/ACTIVE.md index a7acc8bb..0d067394 100644 --- a/dev/four/ACTIVE.md +++ b/dev/four/ACTIVE.md @@ -61,6 +61,7 @@ * JavaScript API audit * Configuration options audit * Maybe add a `:merge` option for things like `hx-indicator`, where you want to merge in parent attribute values? + * hx-disable needs it too * Server Actions (Christian) * Would like to create a new attribute, `hx-config`, which allows a user to override any request config value * Consider making `__createRequestConfig` return a pure string-value object, with all non-string setup done in `handleTriggerEvent`, making diff --git a/www/content/QUIRKS.md b/www/content/QUIRKS.md index 17910d38..64eef2b5 100644 --- a/www/content/QUIRKS.md +++ b/www/content/QUIRKS.md @@ -138,11 +138,10 @@ If you wish to include the values of the enclosing form when issuing an `GET` yo ## History Can Be Tricky -htmx provides support for interacting with the browser's [history](@/docs.md#history). This can be very powerful, but it +TODO fix link +htmx provides support for interacting with the browser's [history](@/docs.md). This can be very powerful, but it can also be tricky, particularly if you are using 3rd party JavaScript libraries that modify the DOM. -There can also be [security concerns](@/docs.md#hx-history) when using htmx's history support. - Most of these issues can be solved by disabling any local history cache and simply issuing a server request when a user navigates backwards in history, with the tradeoff that history navigation will be slower. diff --git a/www/content/_index.md b/www/content/_index.md index 325269db..109730ba 100644 --- a/www/content/_index.md +++ b/www/content/_index.md @@ -100,7 +100,8 @@ if(window.location.search=="?ads=true") {

introduction

-htmx gives you access to [AJAX](@/docs.md#ajax), [CSS Transitions](@/docs.md#css_transitions), [WebSockets](@/docs.md#websockets-and-sse) and [Server Sent Events](@/docs.md#websockets-and-sse) +TODO fix url +htmx gives you access to [AJAX](@/docs.md#ajax), [CSS Transitions](@/docs.md#css_transitions), [WebSockets](@/docs.md) and [Server Sent Events](@/docs.md) directly in HTML, using [attributes](@/reference.md#attributes), so you can build [modern user interfaces](@/examples/_index.md) with the [simplicity](https://en.wikipedia.org/wiki/HATEOAS) and [power](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) of hypertext diff --git a/www/content/attributes/hx-disable.md b/www/content/attributes/hx-disable.md index a88ba73a..e60655bc 100644 --- a/www/content/attributes/hx-disable.md +++ b/www/content/attributes/hx-disable.md @@ -1,14 +1,56 @@ +++ title = "hx-disable" -description = "The hx-disable attribute in htmx will disable htmx processing for a given element and all its children." +description = """\ + The hx-disable attribute in htmx allows you to specify elements that will have the `disabled` attribute added \ + to them for the duration of the request.""" +++ -The `hx-disable` attribute will disable htmx processing for a given element and all its children. This can be -useful as a backup for HTML escaping, when you include user generated content in your site, and you want to -prevent malicious scripting attacks. +The `hx-disable` attribute allows you to specify elements that will have the `disabled` attribute +added to them for the duration of the request. The value of this attribute can be: -The value of the tag is ignored, and it cannot be reversed by any content beneath it. +* A CSS query selector of the element to disable. +* `this` to disable the element itself +* `closest ` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest) + ancestor element or itself, that matches the given CSS selector + (e.g. `closest fieldset` will disable the closest to the element `fieldset`). +* `find ` which will find the first child descendant element that matches the given CSS selector +* `next` which resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling) +* `next ` which will scan the DOM forward for the first element that matches the given CSS selector + (e.g. `next button` will disable the closest following sibling `button` element) +* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling) +* `previous ` which will scan the DOM backwards for the first element that matches the given CSS selector. + (e.g. `previous input` will disable the closest previous sibling `input` element) -## Notes +Here is an example with a button that will disable itself during a request: -* `hx-disable` is inherited +```html + +``` + +When a request is in flight, this will cause the button to be marked with [the `disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled), +which will prevent further clicks from occurring. + +The `hx-disable` attribute also supports specifying multiple CSS selectors separated by commas to disable multiple + elements during the request. Here is an example that disables buttons and text input fields of a particular form during the request: + +```html +
+ + +
+``` + +Note that you can also use the `merge` modifier to merge parent values for a disabled elements and add additional +disabled element CSS selectors: + +```html +
+ ... +
+ + +
+
+``` diff --git a/www/content/attributes/hx-disabled-elt.md b/www/content/attributes/hx-disabled-elt.md deleted file mode 100644 index 3c6845ba..00000000 --- a/www/content/attributes/hx-disabled-elt.md +++ /dev/null @@ -1,62 +0,0 @@ -+++ -title = "hx-disabled-elt" -description = """\ - The hx-disabled-elt attribute in htmx allows you to specify elements that will have the `disabled` attribute added \ - to them for the duration of the request.""" -+++ - -The `hx-disabled-elt` attribute allows you to specify elements that will have the `disabled` attribute -added to them for the duration of the request. The value of this attribute can be: - -* A CSS query selector of the element to disable. -* `this` to disable the element itself -* `closest ` which will find the [closest](https://developer.mozilla.org/docs/Web/API/Element/closest) - ancestor element or itself, that matches the given CSS selector - (e.g. `closest fieldset` will disable the closest to the element `fieldset`). -* `find ` which will find the first child descendant element that matches the given CSS selector -* `next` which resolves to [element.nextElementSibling](https://developer.mozilla.org/docs/Web/API/Element/nextElementSibling) -* `next ` which will scan the DOM forward for the first element that matches the given CSS selector - (e.g. `next button` will disable the closest following sibling `button` element) -* `previous` which resolves to [element.previousElementSibling](https://developer.mozilla.org/docs/Web/API/Element/previousElementSibling) -* `previous ` which will scan the DOM backwards for the first element that matches the given CSS selector. - (e.g. `previous input` will disable the closest previous sibling `input` element) - -Here is an example with a button that will disable itself during a request: - -```html - -``` - -When a request is in flight, this will cause the button to be marked with [the `disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled), -which will prevent further clicks from occurring. - -The `hx-disabled-elt` attribute also supports specifying multiple CSS selectors separated by commas to disable multiple - elements during the request. Here is an example that disables buttons and text input fields of a particular form during the request: - -```html -
- - -
-``` - -Note that you can also use the `inherit` keyword to inherit parent values for a disabled elements and add additional -disabled element CSS selectors: - -```html -
- ... -
- - -
-
-``` - -## Notes - -* `hx-disabled-elt` is inherited and can be placed on a parent element - -[hx-trigger]: https://htmx.org/attributes/hx-trigger/ diff --git a/www/content/attributes/hx-ignore.md b/www/content/attributes/hx-ignore.md new file mode 100644 index 00000000..d358c1bc --- /dev/null +++ b/www/content/attributes/hx-ignore.md @@ -0,0 +1,10 @@ ++++ +title = "hx-ignore" +description = "The hx-ignore attribute in htmx will disable htmx processing for a given element and all its children." ++++ + +The `hx-disable` attribute will disable htmx processing for a given element and all its children. This can be +useful as a backup for HTML escaping, when you include user generated content in your site, and you want to +prevent malicious scripting attacks. + +The value of the tag is ignored, and it cannot be reversed by any content beneath it. diff --git a/www/content/docs.md b/www/content/docs.md index 3b0d22d7..c5dded63 100644 --- a/www/content/docs.md +++ b/www/content/docs.md @@ -29,7 +29,7 @@ custom_classes = "wide-content" * [confirming](#confirming) * [inheritance](#inheritance) * [boosting](#boosting) -* [websockets & SSE](#websockets-and-sse) +* [websockets & SSE](#) * [history](#history) * [requests & responses](#requests) * [validation](#validation) @@ -85,37 +85,30 @@ This tells htmx: htmx extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly within the language: -* Now any element, not just anchors and forms, can issue an HTTP request -* Now any event, not just clicks or form submissions, can trigger requests -* Now any [HTTP verb](https://en.wikipedia.org/wiki/HTTP_Verbs), not just `GET` and `POST`, can be used -* Now any element, not just the entire window, can be the target for update by the request +* Any element can issue an HTTP request +* Any event can trigger requests +* Any [HTTP verb](https://en.wikipedia.org/wiki/HTTP_Verbs) can be used +* Any element can be the target for update by the request -Note that when you are using htmx, on the server side you typically respond with *HTML*, not *JSON*. This keeps you firmly -within the [original web programming model](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm), -using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS) -without even needing to really understand that concept. +Note that when you are using htmx the server typically responds with HTML, not *JSON*. -It's worth mentioning that, if you prefer, you can use the [`data-`](https://html.spec.whatwg.org/multipage/dom.html#attr-data-*) prefix when using htmx: +htmx follows the [original web programming model](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) +of the web, using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS) +without users really needing to understand that concept: just return HTML. -```html -Click Me! -``` +## 2.x to 4.x Migration Guide -If you understand the concepts around htmx and want to see the quirks of the library, please see our -[QUIRKS](@/QUIRKS.md) page. +[Version 2](https://v2.htmx.org) (and [Version 1](https://v1.htmx.org)) of htmx are still supported, but the latest +version of htmx is 4.x. -## 1.x to 2.x Migration Guide - -[Version 1](https://v1.htmx.org) of htmx is still supported and supports IE11, but the latest version of htmx is 2.x. - -If you are migrating to htmx 2.x from [htmx 1.x](https://v1.htmx.org), please see the [htmx 1.x migration guide](@/migration-guide-htmx-1.md). - -If you are migrating to htmx from intercooler.js, please see the [intercooler migration guide](@/migration-guide-intercooler.md). +If you are migrating to htmx 4.x from [htmx 2.x](https://v1.htmx.org), please see the [htmx 2.x migration guide](#). ## Installing -Htmx is a dependency-free, browser-oriented javascript library. This means that using it is as simple as adding a ` + ``` An unminified version is also available as well: ```html - + ``` While the CDN approach is extremely simple, you may want to consider @@ -139,7 +132,7 @@ While the CDN approach is extremely simple, you may want to consider The next easiest way to install htmx is to simply copy it into your project. -Download `htmx.min.js` [from jsDelivr](https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js) and add it to the appropriate directory in your project +Download `htmx.min.js` [from jsDelivr](https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha1/dist/htmx.min.js) and add it to the appropriate directory in your project and include it where necessary with a ` + ``` - ## Attribute Inheritance {#inheritance} -Most attributes in htmx are inherited: they apply to the element they are on as well as any children elements. This -allows you to "hoist" attributes up the DOM to avoid code duplication. Consider the following htmx: +Unlike htmx 2, in htmx 4 attribute inheritance is _explicit_, rather than _implicit_. + +Inheritance allows you to "hoist" attributes up the DOM to avoid code duplication. + +Consider the following htmx: ```html ``` -Here we have a duplicate `hx-confirm` attribute. We can hoist this attribute to a parent element: +Here we have a duplicate `hx-confirm` attribute. + +We can hoist this attribute to a parent element using the `:inherited` modifier: ```html -
+
@@ -744,32 +696,6 @@ Here we have a duplicate `hx-confirm` attribute. We can hoist this attribute to This `hx-confirm` attribute will now apply to all htmx-powered elements within it. -Sometimes you wish to undo this inheritance. Consider if we had a cancel button to this group, but didn't want it to -be confirmed. We could add an `unset` directive on it like so: - -```html -
- - - -
-``` - -The top two buttons would then show a confirm dialog, but the bottom cancel button would not. - -Inheritance can be disabled on a per-element and per-attribute basis using the -[`hx-disinherit`](@/attributes/hx-disinherit.md) attribute. - -If you wish to disable attribute inheritance entirely, you can set the `htmx.config.disableInheritance` configuration -variable to `true`. This will disable inheritance as a default, and allow you to specify inheritance explicitly -with the [`hx-inherit`](@/attributes/hx-inherit.md) attribute. - ## Boosting Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](@/attributes/hx-boost.md) attribute. This @@ -778,17 +704,23 @@ attribute will convert all anchor tags and forms into AJAX requests that, by def Here is an example: ```html -
+ ``` -The anchor tag in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag. +The anchor tags in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag. + +Note that `hx-boost` is using the `inherited` modifier here. ### Progressive Enhancement {#progressive_enhancement} A feature of `hx-boost` is that it degrades gracefully if javascript is not enabled: the links and forms continue -to work, they simply don't use ajax requests. This is known as +to work, they simply don't use ajax requests. + +This is known as [Progressive Enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement), and it allows a wider audience to use your site's functionality. @@ -837,11 +769,14 @@ As such, the normal HTML accessibility recommendations apply. For example: * Associate text labels with all form fields * Maximize the readability of your application with appropriate fonts, contrast, etc. -## Web Sockets & SSE {#websockets-and-sse} +## SSE -Web Sockets and Server Sent Events (SSE) are supported via extensions. Please see -the [SSE extension](/extensions/sse) and [WebSocket extension](/extensions/ws) -pages to learn more. +TODO: Christian, document new SSE functionality + +## Web Sockets + +Web Sockets are supported via and extensions. Please see the [WebSocket extension](/extensions/ws) +page to learn more. ## History Support {#history} @@ -854,156 +789,50 @@ to the browser's history, include the [hx-push-url](@/attributes/hx-push-url.md) Blog ``` -When a user clicks on this link, htmx will snapshot the current DOM and store it before it makes a request to /blog. -It then does the swap and pushes a new location onto the history stack. +When a user clicks on this link, htmx will push a new location onto the history stack. -When a user hits the back button, htmx will retrieve the old content from storage and swap it back into the target, -simulating "going back" to the previous state. If the location is not found in the cache, htmx will make an ajax -request to the given URL, with the header `HX-History-Restore-Request` set to true, and expects back the HTML needed -for the entire page. You should always set `htmx.config.historyRestoreAsHxRequest` to false to prevent the `HX-Request` header -which can then be safely used to respond with partials. Alternatively, if the `htmx.config.refreshOnHistoryMiss` config variable +When a user hits the back button, htmx will retrieve the old content from the original URL and swap it back into the body, +simulating "going back" to the previous state. + +An ajax request with the `HX-History-Restore-Request` set to true, and expects back the HTML needed +for the entire page. + +// TODO - need to revisit if this is an issue still +You should always set `htmx.config.historyRestoreAsHxRequest` to false to prevent the `HX-Request` header +which can then be safely used to respond with partials. +// END TODO - need to revisit if this is an issue still + +Alternatively, if the `htmx.config.refreshOnHistoryMiss` config variable is set to true, it will issue a hard browser refresh. **NOTE:** If you push a URL into the history, you **must** be able to navigate to that URL and get a full page back! -A user could copy and paste the URL into an email, or new tab. Additionally, htmx will need the entire page when restoring -history if the page is not in the history cache. +A user could copy and paste the URL into an email, or new tab. -### Specifying History Snapshot Element - -By default, htmx will use the `body` to take and restore the history snapshot from. This is usually the right thing, but -if you want to use a narrower element for snapshotting you can use the [hx-history-elt](@/attributes/hx-history-elt.md) -attribute to specify a different one. - -Careful: this element will need to be on all pages or restoring from history won't work reliably. - -### Undoing DOM Mutations By 3rd Party Libraries - -If you are using a 3rd party library and want to use the htmx history feature, you will need to clean up the DOM before -a snapshot is taken. Let's consider the [Tom Select](https://tom-select.js.org/) library, which makes select elements -a much richer user experience. Let's set up TomSelect to turn any input element with the `.tomselect` class into a rich -select element. - -First we need to initialize elements that have the class in new content: - -```javascript -htmx.onLoad(function (target) { - // find all elements in the new content that should be - // an editor and init w/ TomSelect - var editors = target.querySelectorAll(".tomselect") - .forEach(elt => new TomSelect(elt)) -}); -``` - -This will create a rich selector for all input elements that have the `.tomselect` class on it. However, it mutates -the DOM and we don't want that mutation saved to the history cache, since TomSelect will be reinitialized when the -history content is loaded back into the screen. - -To deal with this, we need to catch the `htmx:beforeHistorySave` event and clean out the TomSelect mutations by calling -`destroy()` on them: - -```javascript -htmx.on('htmx:beforeHistorySave', function() { - // find all TomSelect elements - document.querySelectorAll('.tomSelect') - .forEach(elt => elt.tomselect.destroy()) // and call destroy() on them -}) -``` - -This will revert the DOM to the original HTML, thus allowing for a clean snapshot. - -### Disabling History Snapshots - -History snapshotting can be disabled for a URL by setting the [hx-history](@/attributes/hx-history.md) attribute to `false` -on any element in the current document, or any html fragment loaded into the current document by htmx. This can be used -to prevent sensitive data entering the localStorage cache, which can be important for shared-use / public computers. -History navigation will work as expected, but on restoration the URL will be requested from the server instead of the -local history cache. +Additionally, htmx will need the entire page when restoring history if the page. ## Requests & Responses {#requests} Htmx expects responses to the AJAX requests it makes to be HTML, typically HTML fragments (although a full HTML -document, matched with a [hx-select](@/attributes/hx-select.md) tag can be useful too). Htmx will then swap the returned -HTML into the document at the target specified and with the swap strategy specified. +document, matched with a [hx-select](@/attributes/hx-select.md) tag can be useful too). + +Htmx will then swap the returned HTML into the document at the target specified and with the swap strategy specified. Sometimes you might want to do nothing in the swap, but still perhaps trigger a client side event ([see below](#response-headers)). For this situation, by default, you can return a `204 - No Content` response code, and htmx will ignore the content of the response. -In the event of an error response from the server (e.g. a 404 or a 501), htmx will trigger the [`htmx:responseError`](@/events.md#htmx:responseError) -event, which you can handle. - -In the event of a connection error, the [`htmx:sendError`](@/events.md#htmx:sendError) event will be triggered. +TODO fix link +In the event of a connection error, the [`htmx:error`](@/events.md) event will be triggered. ### Configuring Response Handling {#response-handling} -You can configure the above behavior of htmx by mutating or replacing the `htmx.config.responseHandling` array. This -object is a collection of JavaScript objects defined like so: - -```js - responseHandling: [ - {code:"204", swap: false}, // 204 - No Content by default does nothing, but is not an error - {code:"[23]..", swap: true}, // 200 & 300 responses are non-errors and are swapped - {code:"[45]..", swap: false, error:true}, // 400 & 500 responses are not swapped and are errors - {code:"...", swap: false} // catch all for any other response code - ] -``` - -When htmx receives a response it will iterate in order over the `htmx.config.responseHandling` array and test if the -`code` property of a given object, when treated as a Regular Expression, matches the current response. If an entry -does match the current response code, it will be used to determine if and how the response will be processed. - -The fields available for response handling configuration on entries in this array are: - -* `code` - a String representing a regular expression that will be tested against response codes. -* `swap` - `true` if the response should be swapped into the DOM, `false` otherwise -* `error` - `true` if htmx should treat this response as an error -* `ignoreTitle` - `true` if htmx should ignore title tags in the response -* `select` - A CSS selector to use to select content from the response -* `target` - A CSS selector specifying an alternative target for the response -* `swapOverride` - An alternative swap mechanism for the response - -#### Configuring Response Handling Examples {#response-handling-examples} - -As an example of how to use this configuration, consider a situation when a server-side framework responds with a -[`422 - Unprocessable Entity`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) response when validation errors occur. By default, htmx will ignore the response, -since it matches the Regular Expression `[45]..`. - -Using the [meta config](#configuration-options) mechanism for configuring responseHandling, we could add the following -config: - -```html - - -``` - -If you wanted to swap everything, regardless of HTTP response code, you could use this configuration: - -```html - -``` - -Finally, it is worth considering using the [Response Targets](/extensions/response-targets) -extension, which allows you to configure the behavior of response codes declaratively via attributes. +TODO: need to figure out how we are gonna handle this (telroshan) ### CORS +TODO: update for fetch() + When using htmx in a cross origin context, remember to configure your web server to set Access-Control headers in order for htmx headers to be visible on the client side. @@ -1017,19 +846,18 @@ on the client side. htmx includes a number of useful headers in requests: -| Header | Description | -|--------|-------------| -| `HX-Boosted` | indicates that the request is via an element using [hx-boost](@/attributes/hx-boost.md) -| `HX-Current-URL` | the current URL of the browser -| `HX-History-Restore-Request` | "true" if the request is for history restoration after a miss in the local history cache -| `HX-Prompt` | the user response to an [hx-prompt](@/attributes/hx-prompt.md) -| `HX-Request` | always "true" except on history restore requests if `htmx.config.historyRestoreAsHxRequest' disabled -| `HX-Target` | the `id` of the target element if it exists -| `HX-Trigger-Name` | the `name` of the triggered element if it exists -| `HX-Trigger` | the `id` of the triggered element if it exists +// TODO which are we going to keep? + +| Header | Description | +|------------------------------|------------------------------------------------------------------------------------------------------| +| `HX-Boosted` | indicates that the request is via an element using [hx-boost](@/attributes/hx-boost.md) | +| `HX-History-Restore-Request` | "true" if the request is for history restoration after a miss in the local history cache | +| `HX-Request` | always "true" except on history restore requests if `htmx.config.historyRestoreAsHxRequest' disabled | ### Response Headers +// TODO which are we going to keep? + htmx supports some htmx-specific response headers: * [`HX-Location`](@/headers/hx-location.md) - allows you to do a client-side redirect that does not do a full page reload @@ -1049,26 +877,13 @@ For more on the `HX-Trigger` headers, see [`HX-Trigger` Response Headers](@/head Submitting a form via htmx has the benefit of no longer needing the [Post/Redirect/Get Pattern](https://en.wikipedia.org/wiki/Post/Redirect/Get). After successfully processing a POST request on the server, you don't need to return a [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). You can directly return the new HTML fragment. -Also the response headers above are not provided to htmx for processing with 3xx Redirect response codes like [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). Instead, the browser will intercept the redirection internally and return the headers and response from the redirected URL. Where possible use alternative response codes like 200 to allow returning of these response headers. +Also, the response headers above are not provided to htmx for processing with 3xx Redirect response codes like [HTTP 302 (Redirect)](https://en.wikipedia.org/wiki/HTTP_302). Instead, the browser will intercept the redirection internally and return the headers and response from the redirected URL. Where possible use alternative response codes like 200 to allow returning of these response headers. ### Request Order of Operations {#request-operations} The order of operations in a htmx request are: -* The element is triggered and begins a request - * Values are gathered for the request - * The `htmx-request` class is applied to the appropriate elements - * The request is then issued asynchronously via AJAX - * Upon getting a response the target element is marked with the `htmx-swapping` class - * An optional swap delay is applied (see the [hx-swap](@/attributes/hx-swap.md) attribute) - * The actual content swap is done - * the `htmx-swapping` class is removed from the target - * the `htmx-added` class is added to each new piece of content - * the `htmx-settling` class is applied to the target - * A settle delay is done (default: 20ms) - * The DOM is settled - * the `htmx-settling` class is removed from the target - * the `htmx-added` class is removed from each new piece of content +// TODO - redo You can use the `htmx-swapping` and `htmx-settling` classes to create [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) between pages. @@ -1076,40 +891,11 @@ You can use the `htmx-swapping` and `htmx-settling` classes to create ## Validation Htmx integrates with the [HTML5 Validation API](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) -and will not issue a request for a form if a validatable input is invalid. This is true for both AJAX requests as well as -WebSocket sends. - -Htmx fires events around validation that can be used to hook in custom validation and error handling: - -* `htmx:validation:validate` - called before an element's `checkValidity()` method is called. May be used to add in - custom validation logic -* `htmx:validation:failed` - called when `checkValidity()` returns false, indicating an invalid input -* `htmx:validation:halted` - called when a request is not issued due to validation errors. Specific errors may be found - in the `event.detail.errors` object +and will not issue a request for a form if a validatable input is invalid. Non-form elements do not validate before they make requests by default, but you can enable validation by setting the [`hx-validate`](@/attributes/hx-validate.md) attribute to "true". -Normal browser form submission alerts the user of any validation errors automatically and auto focuses on the first invalid input. For backwards compatibility reasons htmx does not report the validation to the users by default and you should always enable this option by setting `htmx.config.reportValidityOfForms` to `true` to restore the default browser behavior. - -### Validation Example - -Here is an example of an input that uses the [`hx-on`](/attributes/hx-on) attribute to catch the -`htmx:validation:validate` event and require that the input have the value `foo`: - -```html -
- -
-``` - -Note that all client side validations must be re-done on the server side, as they can always be bypassed. - ## Animations Htmx allows you to use [CSS transitions](#css_transitions) @@ -1119,74 +905,22 @@ Please see the [Animation Guide](@/examples/animations.md) for more details on t ## Extensions -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. +// TODO is this true? + +In htmx 4, extensions are just libraries that hook into the standard events and use the public API. There is no longer +a need for an explicit extension API. ### Core Extensions -htmx supports a few "core" extensions, which are supported by the htmx development team: +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 [Server 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. -### Installing Extensions - -The fastest way to install htmx extensions created by others is to load them via a CDN. Remember to always include the core htmx library before the extensions and [enable the extension](#enabling-extensions). For example, if you would like to use the [response-targets](/extensions/response-targets) extension, you can add this to your head tag: -```HTML - - - - - - ... -``` -An unminified version is also available at `https://cdn.jsdelivr.net/npm/htmx-ext-extension-name/dist/extension-name.js` (replace `extension-name` with the name of the extension). - -While the CDN approach is simple, you may want to consider [not using CDNs in production](https://blog.wesleyac.com/posts/why-not-javascript-cdn). The next easiest way to install htmx extensions is to simply copy them into your project. Download the extension from `https://cdn.jsdelivr.net/npm/htmx-ext-extension-name` (replace `extension-name` with the name of the extension) e.g., https://cdn.jsdelivr.net/npm/htmx-ext-response-targets. Then add it to the appropriate directory in your project and include it where necessary with a ` -``` - -This helper allows you to add mock responses by adding `template` tags with a `url` attribute to indicate which URL. -The response for that url will be the innerHTML of the template, making it easy to construct mock responses. You can -add a delay to the response with a `delay` attribute, which should be an integer indicating the number of milliseconds -to delay - -You may embed simple expressions in the template with the `${}` syntax. - -Note that this should only be used for demos and is in no way guaranteed to work for long periods of time -as it will always be grabbing the latest versions htmx and hyperscript! - -#### Demo Example - -Here is an example of the code in action: - -```html - - - - - - - - - - - -``` - ## Scripting {#scripting} While htmx encourages a hypermedia approach to building web applications, it offers many options for client scripting. Scripting is included in the REST-ful description of web architecture, see: [Code-On-Demand](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_7). As much as is feasible, we recommend a [hypermedia-friendly](/essays/hypermedia-friendly-scripting) approach to scripting in your web application: @@ -1387,23 +1050,11 @@ While htmx encourages a hypermedia approach to building web applications, it off * [Consider inline scripting](/essays/hypermedia-friendly-scripting#inline) The primary integration point between htmx and scripting solutions is the [events](#events) that htmx sends and can -respond to. See the SortableJS example in the [3rd Party Javascript](#3rd-party) section for a good template for +respond to. + +See the SortableJS example in the [3rd Party Javascript](#3rd-party) section for a good template for integrating a JavaScript library with htmx via events. -Scripting solutions that pair well with htmx include: - -* [VanillaJS](http://vanilla-js.com/) - Simply using the built-in abilities of JavaScript to hook in event handlers to - respond to the events htmx emits can work very well for scripting. This is an extremely lightweight and increasingly - popular approach. -* [AlpineJS](https://alpinejs.dev/) - Alpine.js provides a rich set of tools for creating sophisticated front end scripts, - including reactive programming support, while still remaining extremely lightweight. Alpine encourages the "inline scripting" - approach that we feel pairs well with htmx. -* [jQuery](https://jquery.com/) - Despite its age and reputation in some circles, jQuery pairs very well with htmx, particularly - in older code-bases that already have a lot of jQuery in them. -* [hyperscript](https://hyperscript.org) - Hyperscript is an experimental front-end scripting language created by the same - team that created htmx. It is designed to embed well in HTML and both respond to and create events, and pairs very well - with htmx. - We have an entire chapter entitled ["Client-Side Scripting"](https://hypermedia.systems/client-side-scripting/) in [our book](https://hypermedia.systems) that looks at how scripting can be integrated into your htmx-based application. @@ -1419,11 +1070,13 @@ such as `onClick`: ``` This feature allows scripting logic to be co-located with the HTML elements the logic applies to, giving good -[Locality of Behaviour (LoB)](/essays/locality-of-behaviour). Unfortunately, HTML only allows `on*` attributes for a fixed +[Locality of Behaviour (LoB)](/essays/locality-of-behaviour). + +Unfortunately, HTML only allows `on*` attributes for a fixed number of [specific DOM events](https://www.w3schools.com/tags/ref_eventattributes.asp) (e.g. `onclick`) and doesn't provide a generalized mechanism for responding to arbitrary events on elements. -In order to address this shortcoming, htmx offers [`hx-on*`](/attributes/hx-on) attributes. These attributes allow +In order to address this shortcoming, htmx offers [`hx-on:*`](/attributes/hx-on) attributes. These attributes allow you to respond to any event in a manner that preserves the LoB of the standard `on*` properties. If we wanted to respond to the `click` event using an `hx-on` attribute, we would write this: @@ -1437,34 +1090,27 @@ If we wanted to respond to the `click` event using an `hx-on` attribute, we woul So, the string `hx-on`, followed by a colon (or a dash), then by the name of the event. For a `click` event, of course, we would recommend sticking with the standard `onclick` attribute. However, consider an -htmx-powered button that wishes to add a parameter to a request using the `htmx:config-request` event. This would not -be possible using a standard `on*` property, but it can be done using the `hx-on:htmx:config-request` attribute: +htmx-powered button that wishes to add a parameter to a request using the `htmx:config:request` event. This would not +be possible using a standard `on*` property, but it can be done using the `hx-on:htmx:config:request` attribute: +// TODO verify symbols ```html ``` Here the `example` parameter is added to the `POST` request before it is issued, with the value 'Hello Scripting!'. -Another usecase is to [reset user input](@/examples/reset-user-input.md) on successful requests using the `afterRequest` -event, avoiding the need for something like an out of band swap. - -The `hx-on*` attributes are a very simple mechanism for generalized embedded scripting. It is _not_ a replacement for more -fully developed front-end scripting solutions such as AlpineJS or hyperscript. It can, however, augment a VanillaJS-based -approach to scripting in your htmx-powered application. - -Note that HTML attributes are *case insensitive*. This means that, unfortunately, events that rely on capitalization/ -camel casing, cannot be responded to. If you need to support camel case events we recommend using a more fully -functional scripting solution such as AlpineJS or hyperscript. htmx dispatches all its events in both camelCase and in -kebab-case for this very reason. +Another usecase is to [reset user input](@/examples/reset-user-input.md) on successful requests using the `htmx:after:swap` +event, avoiding the need for something like an out-of-band swap. ### 3rd Party Javascript {#3rd-party} -Htmx integrates fairly well with third party libraries. If the library fires events on the DOM, you can use those events to -trigger requests from htmx. +Htmx integrates well with third party libraries. + +If the library fires events on the DOM, you can use those events to trigger requests from htmx. A good example of this is the [SortableJS demo](@/examples/sortable.md): @@ -1479,26 +1125,13 @@ A good example of this is the [SortableJS demo](@/examples/sortable.md): With Sortable, as with most javascript libraries, you need to initialize content at some point. -In jquery you might do this like so: +In htmx, the cleanest way to do this is using the `htmx.onLoad()` method to register a callback. -```javascript -$(document).ready(function() { - var sortables = document.body.querySelectorAll(".sortable"); - for (var i = 0; i < sortables.length; i++) { - var sortable = sortables[i]; - new Sortable(sortable, { - animation: 150, - ghostClass: 'blue-background-class' - }); - } -}); -``` - -In htmx, you would instead use the `htmx.onLoad` function, and you would select only from the newly loaded content, -rather than the entire document: +This callback will be called whenever htmx inserts new content into the DOM, allowing you to initialize +any widgets in the new content. ```js -htmx.onLoad(function(content) { +htmx.onLoad((content) => { var sortables = content.querySelectorAll(".sortable"); for (var i = 0; i < sortables.length; i++) { var sortable = sortables[i]; @@ -1512,40 +1145,6 @@ htmx.onLoad(function(content) { This will ensure that as new content is added to the DOM by htmx, sortable elements are properly initialized. -If javascript adds content to the DOM that has htmx attributes on it, you need to make sure that this content -is initialized with the `htmx.process()` function. - -For example, if you were to fetch some data and put it into a div using the `fetch` API, and that HTML had -htmx attributes in it, you would need to add a call to `htmx.process()` like this: - -```js -let myDiv = document.getElementById('my-div') -fetch('http://example.com/movies.json') - .then(response => response.text()) - .then(data => { myDiv.innerHTML = data; htmx.process(myDiv); } ); -``` - -Some 3rd party libraries create content from HTML template elements. For instance, Alpine JS uses the `x-if` -attribute on templates to add content conditionally. Such templates are not initially part of the DOM and, -if they contain htmx attributes, will need a call to `htmx.process()` after they are loaded. The following -example uses Alpine's `$watch` function to look for a change of value that would trigger conditional content: - -```html -
- - -
-``` - #### Web Components {#web-components} Please see the [Web Components Examples](@/examples/web-components.md) page for examples on how to integrate htmx @@ -1570,6 +1169,8 @@ keyed based on a composite of the response URL and the `HX-Request` request head rather than being based just on the response URL. Always disable `htmx.config.historyRestoreAsHxRequest` so that these history full HTML requests are not cached with partial fragment responses. +// TODO - verify if still needed + If you are unable (or unwilling) to use the `Vary` header, you can alternatively set the configuration parameter `getCacheBusterParam` to `true`. If this configuration variable is set, htmx will include a cache-busting parameter in `GET` requests that it makes, which will prevent browsers from caching htmx-based and non-htmx based responses @@ -1618,29 +1219,23 @@ your web application, and htmx provides tools to help secure your application as Let's take a look at them. -#### `hx-disable` +#### `hx-ignore` -The first tool htmx provides to help further secure your application is the [`hx-disable`](/attributes/hx-disable) +The first tool htmx provides to help further secure your application is the [`hx-ignore`](/attributes/hx-ignore) attribute. This attribute will prevent processing of all htmx attributes on a given element, and on all elements within it. So, for example, if you were including raw HTML content in a template (again, this is not recommended!) then you -could place a div around the content with the `hx-disable` attribute on it: +could place a div around the content with the `hx-ignore` attribute on it: ```html -
+
<%= raw(user_content) %>
``` And htmx will not process any htmx-related attributes or features found in that content. This attribute cannot be -disabled by injecting further content: if an `hx-disable` attribute is found anywhere in the parent hierarchy of an +disabled by injecting further content: if an `hx-ignore` attribute is found anywhere in the parent hierarchy of an element, it will not be processed by htmx. -#### `hx-history` - -Another security consideration is htmx history cache. You may have pages that have sensitive data that you do not -want stored in the users `localStorage` cache. You can omit a given page from the history cache by including the -[`hx-history`](/attributes/hx-history) attribute anywhere on the page, and setting its value to `false`. - #### Configuration Options htmx also provides configuration options related to security: @@ -1648,7 +1243,6 @@ htmx also provides configuration options related to security: * `htmx.config.selfRequestsOnly` - if set to `true`, only requests to the same domain as the current document will be allowed * `htmx.config.allowScriptTags` - htmx will process `