mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-27 11:32:54 +00:00
Merge branch 'master' into dev
This commit is contained in:
commit
533da1c71c
11
README.md
11
README.md
@ -8,15 +8,16 @@
|
|||||||
|
|
||||||
## introduction
|
## introduction
|
||||||
|
|
||||||
htmx is a set of HTML extensions give you to access to [AJAX](https://htmx.org/docs#ajax),
|
htmx allows you to access [AJAX](https://htmx.org/docs#ajax),
|
||||||
[WebSockets](https://htmx.org/docs#websockets) and [Server Sent Events](https://htmx.org/docs#sse)
|
[WebSockets](https://htmx.org/docs#websockets) and [Server Sent Events](https://htmx.org/docs#sse)
|
||||||
via [attributes](https://htmx.org/reference#attributes), allowing you to build [modern UI](https://htmx.org/examples) with the [simplicity](https://en.wikipedia.org/wiki/HATEOAS) and
|
directly in HTML, using [attributes](https://htmx.org/reference#attributes), so you can build
|
||||||
|
[modern user interfaces](https://htmx.org/examples) 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
|
[power](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) of hypertext
|
||||||
|
|
||||||
htmx is small ([~7k min.gz'd](https://unpkg.com/htmx.org/dist/)),
|
htmx is small ([~7k min.gz'd](https://unpkg.com/htmx.org/dist/)),
|
||||||
[dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json),
|
[dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json),
|
||||||
[extendable](https://htmx.org/extensions),
|
[extendable](https://htmx.org/extensions) &
|
||||||
IE11 compatible & you can try it out quickly, without a huge rewrite
|
IE11 compatible
|
||||||
|
|
||||||
## quick start
|
## quick start
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ htmx is the successor to [intercooler.js](http://intercoolerjs.org)
|
|||||||
## contributing
|
## contributing
|
||||||
|
|
||||||
* please write code, including tests, in ES5 for [IE 11 compatibility](https://stackoverflow.com/questions/39902809/support-for-es6-in-internet-explorer-11)
|
* please write code, including tests, in ES5 for [IE 11 compatibility](https://stackoverflow.com/questions/39902809/support-for-es6-in-internet-explorer-11)
|
||||||
* please include test cases in `/test` and docs in `/www`
|
* please include test cases in [`/test`](https://github.com/bigskysoftware/htmx/tree/dev/test) and docs in [`/www`](https://github.com/bigskysoftware/htmx/tree/dev/www)
|
||||||
* if you are adding a feature, consider doing it as an [extension](https://htmx.org/extensions) instead to
|
* if you are adding a feature, consider doing it as an [extension](https://htmx.org/extensions) instead to
|
||||||
keep the core htmx code tidy
|
keep the core htmx code tidy
|
||||||
* development pull requests should be against the `dev` branch, docs fixes can be made directly against `master`
|
* development pull requests should be against the `dev` branch, docs fixes can be made directly against `master`
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
htmx.defineExtension('json-enc', {
|
htmx.defineExtension('json-enc', {
|
||||||
encodeParameters : function(xhr, parameters, elt) {
|
encodeParameters : function(xhr, parameters, elt) {
|
||||||
xhr.requestHeaders['Content-Type'] = 'application/json';
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
xhr.overrideMimeType('text/json');
|
xhr.overrideMimeType('text/json');
|
||||||
return (JSON.stringify(parameters));
|
return (JSON.stringify(parameters));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,12 +54,12 @@ CSS. Here is an example that uses `display` rather than opacity:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the target of the `ic-indicator` selector need not be the exact element that you
|
Note that the target of the `hx-indicator` selector need not be the exact element that you
|
||||||
want to show: it can be any element in the parent hierarchy of the indicator.
|
want to show: it can be any element in the parent hierarchy of the indicator.
|
||||||
|
|
||||||
Finally, note that the `htmx-request` class by default is added to the element causing
|
Finally, note that the `htmx-request` class by default is added to the element causing
|
||||||
the request, so you can place an indicator inside of that element and not need to explictly
|
the request, so you can place an indicator inside of that element and not need to explictly
|
||||||
call it out with the `ic-indicator` attribute:
|
call it out with the `hx-indicator` attribute:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<button hx-post="/example">
|
<button hx-post="/example">
|
||||||
|
|||||||
@ -9,7 +9,7 @@ The `hx-target` attribute allows you to target a different element for swapping
|
|||||||
request. The value of this attribute can be:
|
request. The value of this attribute can be:
|
||||||
|
|
||||||
* a CSS query selector of the element to target
|
* a CSS query selector of the element to target
|
||||||
* `this` which indicates that the element that the `ic-target` attribute is on is the 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.
|
* `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)
|
(e.g. `closest tr` will target the closest table row to the element)
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,7 @@ within the language:
|
|||||||
* Now any element, not just the entire window, can be the target for update by the request
|
* 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 respond with *HTML*, not *JSON*. This keeps you firmly
|
Note that when you are using htmx, on the server side you 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)),
|
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)
|
using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)
|
||||||
without even needing to really understand that concept.
|
without even needing to really understand that concept.
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ Here is a `div` that posts to `/mouse_entered` when a mouse enters it:
|
|||||||
If you want a request to only happen once, you can use the `once` modifier for the trigger:
|
If you want a request to only happen once, you can use the `once` modifier for the trigger:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div hx-post="/mouse_entered" hx-trigger="mouseenter once"">
|
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
|
||||||
[Here Mouse, Mouse!]
|
[Here Mouse, Mouse!]
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
@ -189,7 +189,7 @@ If you want an element to poll the given URL rather than wait for an event, you
|
|||||||
with the [`hx-trigger`](/attributes/hx-trigger/) attribute:
|
with the [`hx-trigger`](/attributes/hx-trigger/) attribute:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div hx-get="/news" trigger="every 2s">
|
<div hx-get="/news" hx-trigger="every 2s">
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -226,7 +226,7 @@ than a single value.
|
|||||||
##### Details
|
##### Details
|
||||||
|
|
||||||
* `detail.parameters` - the parameters that will be submitted in the request
|
* `detail.parameters` - the parameters that will be submitted in the request
|
||||||
* `detail.unfilteredParameters` - the parameters that were found before filtering by [`ic-select`](/attributes/ic-select)
|
* `detail.unfilteredParameters` - the parameters that were found before filtering by [`hx-select`](/attributes/hx-select)
|
||||||
* `detail.headers` - the request headers
|
* `detail.headers` - the request headers
|
||||||
* `detail.elt` - the element that triggered the request
|
* `detail.elt` - the element that triggered the request
|
||||||
* `detail.target` - the target of the request
|
* `detail.target` - the target of the request
|
||||||
|
|||||||
@ -127,12 +127,8 @@ You can see a working examle of this code below.
|
|||||||
|
|
||||||
// templates
|
// templates
|
||||||
function displayUI(contacts) {
|
function displayUI(contacts) {
|
||||||
return `<div hx-include="#checked-contacts" hx-target="#tbody">
|
return `<h3>Select Rows And Activate Or Deactivate Below<h3>
|
||||||
<a class="btn" hx-put="/activate">Activate</a>
|
<form id="checked-contacts">
|
||||||
<a class="btn" hx-put="/deactivate">Deactivate</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form id="checked-contacts">
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -146,7 +142,12 @@ You can see a working examle of this code below.
|
|||||||
${displayTable([], contacts, "")}
|
${displayTable([], contacts, "")}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>`
|
</form>
|
||||||
|
<br/>
|
||||||
|
<div hx-include="#checked-contacts" hx-target="#tbody">
|
||||||
|
<a class="btn" hx-put="/activate">Activate</a>
|
||||||
|
<a class="btn" hx-put="/deactivate">Deactivate</a>
|
||||||
|
</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayTable(ids, contacts, action) {
|
function displayTable(ids, contacts, action) {
|
||||||
|
|||||||
@ -87,7 +87,7 @@ Below is a working demo of this example. The only email that will be accepted i
|
|||||||
|
|
||||||
// routes
|
// routes
|
||||||
init("/demo", function(request, params){
|
init("/demo", function(request, params){
|
||||||
return formTemplate();
|
return demoTemplate();
|
||||||
});
|
});
|
||||||
|
|
||||||
onPost("/contact", function(request, params){
|
onPost("/contact", function(request, params){
|
||||||
@ -106,8 +106,13 @@ Below is a working demo of this example. The only email that will be accepted i
|
|||||||
});
|
});
|
||||||
|
|
||||||
// templates
|
// templates
|
||||||
function formTemplate(page) {
|
function demoTemplate() {
|
||||||
return `<h3>Signup Form</h3><form ic-post-to="/contact">
|
|
||||||
|
return `<h3>Signup Form</h3><p>Enter an email into the input below and on tab out it will be validated. Only "test@test.com" will pass.</p> ` + formTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formTemplate() {
|
||||||
|
return `<form hx-post="/contact">
|
||||||
<div hx-target="this" hx-swap="outerHTML">
|
<div hx-target="this" hx-swap="outerHTML">
|
||||||
<label>Email Address</label>
|
<label>Email Address</label>
|
||||||
<input name="email" hx-get="/contact/email" hx-indicator="#ind">
|
<input name="email" hx-get="/contact/email" hx-indicator="#ind">
|
||||||
@ -121,7 +126,7 @@ Below is a working demo of this example. The only email that will be accepted i
|
|||||||
<label>Last Name</label>
|
<label>Last Name</label>
|
||||||
<input type="text" class="form-control" name="lastName">
|
<input type="text" class="form-control" name="lastName">
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-default">Submit</button>
|
<button class="btn btn-default" disabled>Submit</button>
|
||||||
</form>`;
|
</form>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,12 @@ To use an extension you use the [hx-ext](/attributes/hx-ext) attribute:
|
|||||||
Note that the `hx-ext` tag may be placed on parent elements if you want a plugin to apply to an entire swath of the dom,
|
Note that the `hx-ext` tag may be placed on parent elements if you want a plugin to apply to an entire swath of the dom,
|
||||||
and on the `body` tag for it to apply to all htmx requests.
|
and on the `body` tag for it to apply to all htmx requests.
|
||||||
|
|
||||||
|
**Tip:** To use multiple extensions on one element, separate them with a comma:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<button hx-post="/example" hx-ext="debug, json-enc">This Button Uses Two Extensions</button>
|
||||||
|
```
|
||||||
|
|
||||||
## <a name="included"></a> [Included Extensions](#included)
|
## <a name="included"></a> [Included Extensions](#included)
|
||||||
|
|
||||||
The following extensions that are tested and distributed with htmx:
|
The following extensions that are tested and distributed with htmx:
|
||||||
|
|||||||
@ -13,7 +13,7 @@ swapped into the DOM. Currently three client-side templating engines are suppor
|
|||||||
* [nunjucks](https://mozilla.github.io/nunjucks/)
|
* [nunjucks](https://mozilla.github.io/nunjucks/)
|
||||||
|
|
||||||
When you add this extension on an element, any element below it in the DOM can use one of three attributes named
|
When you add this extension on an element, any element below it in the DOM can use one of three attributes named
|
||||||
`<template-engine>-temlpate` (e.g. `mustache-template`) with a template ID, and the extension will resolve and render
|
`<template-engine>-template` (e.g. `mustache-template`) with a template ID, and the extension will resolve and render
|
||||||
the template the standard way for that template engine:
|
the template the standard way for that template engine:
|
||||||
|
|
||||||
* `mustache` - looks a mustache <script> tag up by ID for the template content
|
* `mustache` - looks a mustache <script> tag up by ID for the template content
|
||||||
|
|||||||
@ -13,7 +13,6 @@ will be evaluated as the fields in a javascript object literal.
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<div hx-ext="include-vals">
|
<div hx-ext="include-vals">
|
||||||
<!-- Removes this div after 1 second -->
|
|
||||||
<div hx-get="/test" include-vals="included:true, computed: computeValue()">
|
<div hx-get="/test" include-vals="included:true, computed: computeValue()">
|
||||||
Will Include Additional Values
|
Will Include Additional Values
|
||||||
</div>
|
</div>
|
||||||
@ -22,4 +21,4 @@ will be evaluated as the fields in a javascript object literal.
|
|||||||
|
|
||||||
### Source
|
### Source
|
||||||
|
|
||||||
<https://unpkg.com/htmx.org/dist/ext/remove-me.js>
|
<https://unpkg.com/htmx.org/dist/ext/include-vals.js>
|
||||||
|
|||||||
@ -11,15 +11,16 @@ title: </> htmx - high power tools for html
|
|||||||
|
|
||||||
## introduction
|
## introduction
|
||||||
|
|
||||||
htmx is a set of HTML extensions give you to access to [AJAX](https://htmx.org/docs#ajax),
|
htmx allows you to access [AJAX](https://htmx.org/docs#ajax),
|
||||||
[WebSockets](https://htmx.org/docs#websockets) and [Server Sent Events](https://htmx.org/docs#sse)
|
[WebSockets](https://htmx.org/docs#websockets) and [Server Sent Events](https://htmx.org/docs#sse)
|
||||||
via [attributes](https://htmx.org/reference#attributes), allowing you to build [modern UI](https://htmx.org/examples) with the [simplicity](https://en.wikipedia.org/wiki/HATEOAS) and
|
directly in HTML, using [attributes](https://htmx.org/reference#attributes), so you can build
|
||||||
|
[modern user interfaces](https://htmx.org/examples) 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
|
[power](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) of hypertext
|
||||||
|
|
||||||
htmx is small ([~7k min.gz'd](https://unpkg.com/htmx.org/dist/)),
|
htmx is small ([~7k min.gz'd](https://unpkg.com/htmx.org/dist/)),
|
||||||
[dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json),
|
[dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json),
|
||||||
[extendable](/extensions),
|
[extendable](https://htmx.org/extensions) &
|
||||||
IE11 compatible & you can try it out quickly, without a huge rewrite
|
IE11 compatible
|
||||||
|
|
||||||
## quick start
|
## quick start
|
||||||
|
|
||||||
|
|||||||
91
www/locality-of-behaviour.md
Normal file
91
www/locality-of-behaviour.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
layout: layout.njk
|
||||||
|
title: </> htmx - high power tools for html
|
||||||
|
---
|
||||||
|
## Locality of Behaviour (LoB)
|
||||||
|
|
||||||
|
> "The primary feature for easy maintenance is locality: Locality is that characteristic of source code that enables a
|
||||||
|
> programmer to understand that source by looking at only a small portion of it." -- [Richard Gabriel](https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf)
|
||||||
|
|
||||||
|
### The LoB Principle:
|
||||||
|
|
||||||
|
> The behaviour of a code unit should be as obvious as possible by looking only at that unit of code
|
||||||
|
|
||||||
|
### Discussion
|
||||||
|
|
||||||
|
The LoB principle is a simple prescriptive formulation of the quoted statement from [Richard Gabriel](https://www.dreamsongs.com).
|
||||||
|
In as much as it is possible, and in balance with other concerns, developers should strive to make the behaviour of
|
||||||
|
a code element obvious on inspection.
|
||||||
|
|
||||||
|
Consider two different implementations of an AJAX request in HTML, the first in [htmx](https://htmx.org):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div hx-get="/clicked">Click Me</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
and the second in [jQuery](https://jquery.com/):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$("#d1").on("click", function(){
|
||||||
|
$.ajax({
|
||||||
|
...
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div id="d1">Click Me</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
In the former, the behaviour of the `div` element is obvious on inspection, satisfying the LoB principle.
|
||||||
|
|
||||||
|
In the latter, the behaviour of the `div` element is spread out amongst multiple files. It is difficult to know
|
||||||
|
exactly what the div does without a total knowledge of the code base.
|
||||||
|
|
||||||
|
#### Surfacing Behaviour vs. Inlining Implementation
|
||||||
|
|
||||||
|
A common conflation is surfacing behaviour with inlining implementation of that behaviour. These are separate concepts
|
||||||
|
and, while inlining the implementation of a behaviour isn't *always* incorrect, it may *often* be incorrect.
|
||||||
|
|
||||||
|
Increasing the obviousness of the behaviour of an element is, ceteris paribus, a good thing, but it falls to both end-developers
|
||||||
|
and especially framework developers to make LoB as easy as possible.
|
||||||
|
|
||||||
|
#### Conflict With Other Development Principles
|
||||||
|
|
||||||
|
The LoB will often conflict with other software development principles. Two important ones
|
||||||
|
are:
|
||||||
|
|
||||||
|
* [DRY - Don't Repeat Yourself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)
|
||||||
|
|
||||||
|
Software developers typically strive to avoid redundancy in their code or data. This as come to be called "Staying DRY",
|
||||||
|
i.e. Don't Repeat Yourself. Like other software design principles this, on its own, is a good thing. htmx, for example,
|
||||||
|
allows you to place many attributes on parent elements in a DOM and avoid repeating these attributes on children. This is a
|
||||||
|
violation of LoB, in favor of DRY, and such tradeoffs need to be made judiciously by developers.
|
||||||
|
|
||||||
|
Note that the further behaviour gets from the code unit it effects, the more severe the violation of LoB. If it is
|
||||||
|
within a few lines of the code unit, this is less serious than if it is a page away, which is less serious than if
|
||||||
|
it is in a separate file entirely.
|
||||||
|
|
||||||
|
There is no hard and fast rule, but rather subjective tradeoffs that must be made as software developers.
|
||||||
|
|
||||||
|
* [SoC - Separation Of Concerns](https://en.wikipedia.org/wiki/Separation_of_concerns)
|
||||||
|
|
||||||
|
Separation of concerns a design principle for separating a computer program into distinct sections such that each
|
||||||
|
section addresses a separate concern. A canonical example of this is CSS vs. Javascript. Again, on its own and
|
||||||
|
in isolation this may, indeed, be a good thing. Inlining styles has become more prevalent lately, but there are
|
||||||
|
still strong arguments in favor of SoC in this regard.
|
||||||
|
|
||||||
|
Note that SoC is, however, in conflict with LoB. By tweaking a CSS file the look and, to an extent, behaviour of an
|
||||||
|
element can change dramatically, and it is not obvious where this dramatic change came from. Tools help to an extent
|
||||||
|
here, but there is still "spooky action at a distance" going on.
|
||||||
|
|
||||||
|
Again, this isn't to condemn SoC wholesale, just to say that there are subjective tradeoffs that must be made when
|
||||||
|
considering how to structure your code. The fact that inline styles have become more prevalent as of late is an
|
||||||
|
indication that SoC is losing some support amongst developers.
|
||||||
|
|
||||||
|
#### Conclusion
|
||||||
|
|
||||||
|
LoB is a software design principle that can help make a code bases more humane and maintainable. It must be traded
|
||||||
|
off against other design principles and be considered in terms of the limitations of the system a code unit is
|
||||||
|
written in, but, as much as is it is practical, adherence to this principle will increase your developer productivity
|
||||||
|
and well-being.
|
||||||
@ -62,7 +62,7 @@ title: </> htmx - Attributes
|
|||||||
| `X-HX-Active-Element` | the `id` of the active element if it exists
|
| `X-HX-Active-Element` | the `id` of the active element if it exists
|
||||||
| `X-HX-Current-URL` | the current URL of the browser
|
| `X-HX-Current-URL` | the current URL of the browser
|
||||||
| `X-HX-Event-Target` | the `id` of the original event target
|
| `X-HX-Event-Target` | the `id` of the original event target
|
||||||
| `X-HX-Prompt` | the user response to an [ic-prompt](/attributes/hx-prompt)
|
| `X-HX-Prompt` | the user response to an [hx-prompt](/attributes/hx-prompt)
|
||||||
| `X-HX-Request` | always `true`
|
| `X-HX-Request` | always `true`
|
||||||
| `X-HX-Target` | the `id` of the target element if it exists
|
| `X-HX-Target` | the `id` of the target element if it exists
|
||||||
| `X-HX-Trigger-Name` | the `name` of the triggered element if it exists
|
| `X-HX-Trigger-Name` | the `name` of the triggered element if it exists
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user