274 lines
No EOL
16 KiB
Markdown
274 lines
No EOL
16 KiB
Markdown
---
|
||
website: https://htmx.org/
|
||
obj: concept
|
||
---
|
||
# htmx
|
||
htmx is a library that allows you to access modern browser features directly from [HTML](HTML.md), rather than using javascript.
|
||
|
||
htmx extends and generalizes the core idea of [HTML](HTML.md) as a hypertext, opening up many more possibilities directly within the language:
|
||
|
||
- Now any element, not just anchors and forms, can issue an [HTTP](HTTP.md) request
|
||
- Now any event, not just clicks or form submissions, can trigger requests
|
||
- Now any [HTTP](HTTP.md) verb, 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
|
||
|
||
> **Note:** that when you are using htmx, on the server side you typically respond with _[HTML](HTML.md)_, not _[JSON](../files/JSON.md)_. 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.
|
||
|
||
# Installing
|
||
|
||
Htmx is a dependency-free, browser-oriented javascript library. This means that using it is as simple as adding a `<script>` tag to your document head. No need for complicated build steps or systems.
|
||
|
||
Via CDN:
|
||
```html
|
||
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
|
||
```
|
||
|
||
## Download a copy
|
||
|
||
The next easiest way to install htmx is to simply copy it into your project.
|
||
|
||
Download `htmx.min.js` [from unpkg.com](https://unpkg.com/htmx.org/dist/htmx.min.js) and add it to the appropriate directory in your project and include it where necessary with a `<script>` tag:
|
||
```html
|
||
<script src="/path/to/htmx.min.js"></script>
|
||
```
|
||
|
||
You can also add extensions this way, by downloading them from the `ext/` directory.
|
||
|
||
# AJAX
|
||
The core of htmx is a set of attributes that allow you to issue AJAX requests directly from [HTML](HTML.md):
|
||
|
||
|Attribute|Description|
|
||
|---|---|
|
||
|[hx-get](https://htmx.org/attributes/hx-get/)|Issues a `GET` request to the given URL|
|
||
|[hx-post](https://htmx.org/attributes/hx-post/)|Issues a `POST` request to the given URL|
|
||
|[hx-put](https://htmx.org/attributes/hx-put/)|Issues a `PUT` request to the given URL|
|
||
|[hx-patch](https://htmx.org/attributes/hx-patch/)|Issues a `PATCH` request to the given URL|
|
||
|[hx-delete](https://htmx.org/attributes/hx-delete/)|Issues a `DELETE` request to the given URL|
|
||
|
||
Each of these attributes takes a URL to issue an AJAX request to. The element will issue a request of the specified type to the given URL when the element is [triggered](https://htmx.org/docs/#triggers):
|
||
```html
|
||
<div hx-put="/messages">
|
||
Put To Messages
|
||
</div>
|
||
```
|
||
|
||
This tells the browser:
|
||
> When a user clicks on this div, issue a PUT request to the URL /messages and load the response into the div
|
||
|
||
### Triggering Requests
|
||
By default, AJAX requests are triggered by the “natural” event of an element:
|
||
- `input`, `textarea` & `select` are triggered on the `change` event
|
||
- `form` is triggered on the `submit` event
|
||
- everything else is triggered by the `click` event
|
||
|
||
If you want different behavior you can use the [hx-trigger](https://htmx.org/attributes/hx-trigger/) attribute to specify which event will cause the request.
|
||
|
||
#### Trigger Modifiers
|
||
A trigger can also have a few additional modifiers that change its behavior. For example, if you want a request to only happen once, you can use the `once` modifier for the trigger:
|
||
```html
|
||
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
|
||
[Here Mouse, Mouse!]
|
||
</div>
|
||
```
|
||
|
||
Other modifiers you can use for triggers are:
|
||
- `changed` - only issue a request if the value of the element has changed
|
||
- `delay:<time interval>` - wait the given amount of time (e.g. `1s`) before issuing the request. If the event triggers again, the countdown is reset.
|
||
- `throttle:<time interval>` - wait the given amount of time (e.g. `1s`) before issuing the request. Unlike `delay` if a new event occurs before the time limit is hit the event will be discarded, so the request will trigger at the end of the time period.
|
||
- `from:<CSS Selector>` - listen for the event on a different element. This can be used for things like keyboard shortcuts.
|
||
|
||
You can use these attributes to implement many common UX patterns, such as [Active Search](https://htmx.org/examples/active-search/):
|
||
```html
|
||
<input type="text" name="q"
|
||
hx-get="/trigger_delay"
|
||
hx-trigger="keyup changed delay:500ms"
|
||
hx-target="#search-results"
|
||
placeholder="Search..."
|
||
>
|
||
<div id="search-results"></div>
|
||
```
|
||
This input will issue a request 500 milliseconds after a key up event if the input has been changed and inserts the results into the `div` with the id `search-results`.
|
||
|
||
Multiple triggers can be specified in the [hx-trigger](https://htmx.org/attributes/hx-trigger/) attribute, separated by commas.
|
||
|
||
#### Special Events
|
||
htmx provides a few special events for use in [hx-trigger](https://htmx.org/attributes/hx-trigger/):
|
||
|
||
- `load` - fires once when the element is first loaded
|
||
- `revealed` - fires once when an element first scrolls into the viewport
|
||
- `intersect` - fires once when an element first intersects the viewport. This supports two additional options:
|
||
- `root:<selector>` - a CSS selector of the root element for intersection
|
||
- `threshold:<float>` - a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
|
||
|
||
You can also use custom events to trigger requests if you have an advanced use case.
|
||
|
||
#### Polling
|
||
If you want an element to poll the given URL rather than wait for an event, you can use the `every` syntax with the [`hx-trigger`](https://htmx.org/attributes/hx-trigger/) attribute:
|
||
```html
|
||
<div hx-get="/news" hx-trigger="every 2s"></div>
|
||
```
|
||
|
||
This tells htmx
|
||
> Every 2 seconds, issue a GET to /news and load the response into the div
|
||
|
||
If you want to stop polling from a server response you can respond with the [HTTP](HTTP.md) response code [`286`](https://en.wikipedia.org/wiki/86_(term)) and the element will cancel the polling.
|
||
|
||
#### Load Polling
|
||
Another technique that can be used to achieve polling in htmx is “load polling”, where an element specifies a `load` trigger along with a delay, and replaces itself with the response:
|
||
```html
|
||
<div hx-get="/messages"
|
||
hx-trigger="load delay:1s"
|
||
hx-swap="outerHTML"
|
||
>
|
||
</div>
|
||
```
|
||
If the `/messages` end point keeps returning a div set up this way, it will keep “polling” back to the URL every second.
|
||
|
||
Load polling can be useful in situations where a poll has an end point at which point the polling terminates, such as when you are showing the user a [progress bar](https://htmx.org/examples/progress-bar/).
|
||
|
||
### Targets
|
||
If you want the response to be loaded into a different element other than the one that made the request, you can use the [hx-target](https://htmx.org/attributes/hx-target/) attribute, which takes a CSS selector. Looking back at our Live Search example:
|
||
```html
|
||
<input type="text" name="q"
|
||
hx-get="/trigger_delay"
|
||
hx-trigger="keyup delay:500ms changed"
|
||
hx-target="#search-results"
|
||
placeholder="Search..."
|
||
>
|
||
<div id="search-results"></div>
|
||
```
|
||
You can see that the results from the search are going to be loaded into `div#search-results`, rather than into the input tag.
|
||
|
||
#### Extended CSS Selectors
|
||
`hx-target`, and most attributes that take a CSS selector, support an “extended” CSS syntax:
|
||
- You can use the `this` keyword, which indicates that the element that the `hx-target` attribute is on is the target
|
||
- The `closest <CSS selector>` syntax 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 tr` will target the closest table row to the element)
|
||
- The `next <CSS selector>` syntax will find the next element in the DOM matching the given CSS selector.
|
||
- The `previous <CSS selector>` syntax will find the previous element in the DOM the given CSS selector.
|
||
- `find <CSS selector>` which will find the first child descendant element that matches the given CSS selector. (e.g `find tr` would target the first child descendant row to the element)
|
||
|
||
### Swapping
|
||
htmx offers a few different ways to swap the [HTML](HTML.md) returned into the DOM. By default, the content replaces the `innerHTML` of the target element. You can modify this by using the [hx-swap](https://htmx.org/attributes/hx-swap/) attribute with any of the following values:
|
||
|
||
|Name|Description|
|
||
|---|---|
|
||
|`innerHTML`|the default, puts the content inside the target element|
|
||
|`outerHTML`|replaces the entire target element with the returned content|
|
||
|`afterbegin`|prepends the content before the first child inside the target|
|
||
|`beforebegin`|prepends the content before the target in the targets parent element|
|
||
|`beforeend`|appends the content after the last child inside the target|
|
||
|`afterend`|appends the content after the target in the targets parent element|
|
||
|`delete`|deletes the target element regardless of the response|
|
||
|`none`|does not append content from response ([Out of Band Swaps](https://htmx.org/docs/#oob_swaps) and [Response Headers](https://htmx.org/docs/#response-headers) will still be processed)|
|
||
|
||
### Parameters
|
||
By default, an element that causes a request will include its value if it has one. If the element is a form it will include the values of all inputs within it.
|
||
|
||
As with [HTML](HTML.md) forms, the `name` attribute of the input is used as the parameter name in the request that htmx sends.
|
||
|
||
Additionally, if the element causes a non-`GET` request, the values of all the inputs of the nearest enclosing form will be included.
|
||
|
||
If you wish to include the values of other elements, you can use the [hx-include](https://htmx.org/attributes/hx-include/) attribute with a CSS selector of all the elements whose values you want to include in the request.
|
||
|
||
If you wish to filter out some parameters you can use the [hx-params](https://htmx.org/attributes/hx-params/) attribute.
|
||
|
||
Finally, if you want to programmatically modify the parameters, you can use the [htmx:configRequest](https://htmx.org/events/#htmx:configRequest) event.
|
||
|
||
#### Extra Values
|
||
You can include extra values in a request using the [hx-vals](https://htmx.org/attributes/hx-vals/) (name-expression pairs in [JSON](../files/JSON.md) format) and [hx-vars](https://htmx.org/attributes/hx-vars/) attributes (comma-separated name-expression pairs that are dynamically computed).
|
||
|
||
## Attribute 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:
|
||
```html
|
||
<button hx-delete="/account" hx-confirm="Are you sure?">
|
||
Delete My Account
|
||
</button>
|
||
<button hx-put="/account" hx-confirm="Are you sure?">
|
||
Update My Account
|
||
</button>
|
||
```
|
||
|
||
Here we have a duplicate `hx-confirm` attribute. We can hoist this attribute to a parent element:
|
||
```html
|
||
<div hx-confirm="Are you sure?">
|
||
<button hx-delete="/account">
|
||
Delete My Account
|
||
</button>
|
||
<button hx-put="/account">
|
||
Update My Account
|
||
</button>
|
||
</div>
|
||
```
|
||
|
||
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
|
||
<div hx-confirm="Are you sure?">
|
||
<button hx-delete="/account">
|
||
Delete My Account
|
||
</button>
|
||
<button hx-put="/account">
|
||
Update My Account
|
||
</button>
|
||
<button hx-confirm="unset" hx-get="/">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
```
|
||
The top two buttons would then show a confirm dialog, but the bottom cancel button would not.
|
||
|
||
Automatic inheritance can be disabled using the [`hx-disinherit`](https://htmx.org/attributes/hx-disinherit/) attribute.
|
||
|
||
## Boosting
|
||
Htmx supports “boosting” regular [HTML](HTML.md) anchors and forms with the [hx-boost](https://htmx.org/attributes/hx-boost/) attribute. This attribute will convert all anchor tags and forms into AJAX requests that, by default, target the body of the page.
|
||
|
||
Here is an example:
|
||
```html
|
||
<div hx-boost="true">
|
||
<a href="/blog">Blog</a>
|
||
</div>
|
||
```
|
||
|
||
The anchor tag in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag.
|
||
|
||
## History Support
|
||
Htmx provides a simple mechanism for interacting with the [browser history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API):
|
||
|
||
If you want a given element to push its request URL into the browser navigation bar and add the current state of the page to the browser’s history, include the [hx-push-url](https://htmx.org/attributes/hx-push-url/) attribute:
|
||
```html
|
||
<a hx-get="/blog" hx-push-url="true">Blog</a>
|
||
```
|
||
|
||
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 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](HTML.md) needed for the entire page. 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.
|
||
|
||
# Request & Responses
|
||
### Request Headers
|
||
htmx includes a number of useful headers in requests:
|
||
|
||
| Header | Description |
|
||
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||
| `HX-Request` | will be set to “true” |
|
||
| `HX-Trigger` | will be set to the id of the element that triggered the request |
|
||
| `HX-Trigger-Name` | will be set to the name of the element that triggered the request |
|
||
| `HX-Target` | will be set to the id of the target element |
|
||
| `HX-Prompt` | will be set to the value entered by the user when prompted via [hx-prompt](https://htmx.org/attributes/hx-prompt/) |
|
||
|
||
### Response Headers
|
||
htmx supports some htmx-specific response headers:
|
||
|
||
- `HX-Push` - pushes a new URL into the browser’s address bar
|
||
- `HX-Redirect` - triggers a client-side redirect to a new location
|
||
- `HX-Location` - triggers a client-side redirect to a new location that acts as a swap
|
||
- `HX-Refresh` - if set to “true” the client side will do a full refresh of the page
|
||
- `HX-Trigger` - triggers client side events
|
||
- `HX-Trigger-After-Swap` - triggers client side events after the swap step
|
||
- `HX-Trigger-After-Settle` - triggers client side events after the settle step
|
||
|
||
For more on the `HX-Trigger` headers, see [`HX-Trigger` Response Headers](https://htmx.org/headers/hx-trigger/).
|
||
|
||
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](HTML.md) fragment. |