htmx.org 1.4.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,82 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.0] - 2022-02-2
4
+
5
+ * The new [`hx-sync`](/attributes/hx-sync) attribute allows you to synchronize multiple element requests on a single
6
+ element using various strategies (e.g. replace)
7
+ * You can also now abort an element making a request by sending it the `htmx:abort` event
8
+ * [Server Sent Events](/extensions/server-sent-events) and [Web Sockets](/extensions/web-sockets) are now available as
9
+ extensions, in addition to the normal core support. In htmx 2.0, the current `hx-sse` and `hx-ws` attributes will be
10
+ moved entirely out to these new extensions. By moving these features to extensions we will be able to add functionality
11
+ to both of them without compromising the core file size of htmx. You are encouraged to move over to the new
12
+ extensions, but `hx-sse` and `hx-ws` will continue to work indefinitely in htmx 1.x.
13
+ * You can now mask out [attribute inheritance](/docs#inheritance) via the [`hx-disinherit`](/attributes/hx-disinherit) attribute.
14
+ * The `HX-Push` header can now have the `false` value, which will prevent a history snapshot from occuring.
15
+ * Many new extensions, with a big thanks to all the contributors!
16
+ * A new [`alpine-morph`](/extensions/alpine-morph) allows you to use Alpine's swapping engine, which preserves Alpine
17
+ * A [restored](/extensions/restored) extension was added that will trigger a `restore` event on all elements in the DOM
18
+ on history restoration.
19
+ * A [loading-states](/extensions/loading-states) extension was added that allows you to easily manage loading states
20
+ while a request is in flight, including disabling elements, and adding and removing CSS classes.
21
+ * The `this` symbol now resolves properly for the [`hx-include`](/attributes/hx-include) and [`hx-indicator`](/attributes/hx-indicator)
22
+ attributes
23
+ * When an object is included via the [`hx-vals`](/attributes/hx-vals) attribute, it will be converted to JSON (rather
24
+ than rendering as the string `[Object object]"`)
25
+ * You can now pass a swap style in to the `htmx.ajax()` function call.
26
+ * Poll events now contain a `target` attribute, allowing you to filter a poll on the element that is polling.
27
+ * Two new Out Of Band-related events were added: `htmx:oobBeforeSwap` & `htmx:oobAfterSwap`
28
+
29
+ ## [1.6.1] - 2021-11-22
30
+
31
+ * A new `HX-Retarget` header allows you to change the default target of returned content
32
+ * The `htmx:beforeSwap` event now includes another configurable property: `detail.isError` which can
33
+ be used to indicate if a given response should be treated as an error or not
34
+ * The `htmx:afterRequest` event has two new detail properties: `success` and `failed`, allowing you to write
35
+ trigger filters in htmx or hyperscript:
36
+ ```applescript
37
+ on htmx:afterRequest[failed]
38
+ set #myCheckbox's checked to true
39
+ ```
40
+ * Fixed the `from:` option in [`hx-trigger`](/attributes/hx-trigger) to support `closest <CSS selector>`
41
+ and `find <CSS selector>` forms
42
+ * Don't boost anchor tags with an explicit `target` set
43
+ * Don't cancel all events on boosted elements, only the events that naturally trigger them (click for anchors, submit
44
+ for forms)
45
+ * Persist revealed state in the DOM so that on history navigation, revealed elements are not re-requested
46
+ * Process all [`hx-ext`](/attributes/hx-ext) attributes, even if no other htmx attribute is on the element
47
+ * Snapshot the current URL on load so that history support works properly after a page refresh occurs
48
+ * Many, many documentation updates (thank you to all the contributors!)
49
+
50
+
51
+ ## [1.6.0] - 2021-10-01
52
+
53
+ * Completely reworked `<script>` tag support that now supports the `<script src="...'/>` form
54
+ * You can now use the value `unset` to clear a property that would normally be inherited (e.g. hx-confirm)
55
+ * The `htmx-added` class is added to new content before a swap and removed after the settle phase, which allows you
56
+ more flexibility in writing CSS transitions for added content (rather than relying on the target, as with `htmx-settling`)
57
+ * The `htmx:beforeSwap` event has been updated to allow you to [configure swapping](https://htmx.org/docs/#modifying_swapping_behavior_with_events)
58
+ behavior
59
+ * Improved `<title>` extraction support
60
+ * You can listen to events on the `window` object using the `from:` modifier in `hx-trigger`
61
+ * The `root` option of the `intersect` event was fixed
62
+ * Boosted forms respect the `enctype` declaration
63
+ * The `HX-Boosted` header will be sent on requests from boosted elements
64
+ * Promises are not returned from the main ajax function unless it is an api call (i.e. `htmx.ajax`)
65
+
66
+ ## [1.5.0] - 2021-7-12
67
+
68
+ * Support tracking of button clicked during a form submission
69
+ * Conditional polling via the [hx-trigger](https://htmx.org/attributes/hx-trigger) attribute
70
+ * `document` is now a valid pseudo-selector on the [hx-trigger](https://htmx.org/attributes/hx-trigger) `from:` argument, allowing you
71
+ to listen for events on the document.
72
+ * Added the [hx-request](https://htmx.org/attributes/hx-request) attribute, allowing you to configure the following aspects of the request
73
+ * `timeout` - the timeout of the request
74
+ * `credentials` - if the request will send credentials
75
+ * `noHeaders` - strips all headers from the request
76
+ * Along with the above attribute, you can configure the default values for each of these via the corresponding `htmx.config`
77
+ properties (e.g. `htmx.config.timeout`)
78
+ * Both the `scroll` and `show` options on [hx-swap](https://htmx.org/attributes/hx-swap) now support extended syntax for selecting the
79
+ element to scroll or to show, including the pseudo-selectors `window:top` and `window:bottom`.
3
80
 
4
81
  ## [1.4.1] - 2021-6-1
5
82
 
@@ -7,13 +84,13 @@
7
84
 
8
85
  ## [1.4.0] - 2021-5-25
9
86
 
10
- * Added the `queue` option to the [hx-trigger](/attributes/hx-trigger) attribute, allowing you to specify how events
87
+ * Added the `queue` option to the [hx-trigger](https://htmx.org/attributes/hx-trigger) attribute, allowing you to specify how events
11
88
  should be queued when they are received with a request in flight
12
89
  * The `htmx.config.useTemplateFragments` option was added, allowing you to use HTML template tags for parsing content
13
90
  from the server. This allows you to use Out of Band content when returning things like table rows, but it is not
14
91
  IE11 compatible.
15
92
  * The `defaultSettleDelay` was dropped to 20ms from 100ms
16
- * Introduced a new synthetic event, [intersect](/docs#pecial-events) that allows you to trigger when an item is scrolled into view
93
+ * Introduced a new synthetic event, [intersect](https://htmx.org/docs#pecial-events) that allows you to trigger when an item is scrolled into view
17
94
  as specified by the `IntersectionObserver` API
18
95
  * Fixed timing issue that caused exceptions in the `reveal` logic when scrolling at incredible speeds - <https://github.com/bigskysoftware/htmx/issues/463>
19
96
  * Fixed bug causing SVG titles to be incorrectly used as page title - <https://github.com/bigskysoftware/htmx/issues/459>
@@ -29,7 +106,7 @@
29
106
 
30
107
  ## [1.3.3] - 2021-4-5
31
108
 
32
- * Added the [`hx-disabled`](/docs#security) attribute to allow htmx to be turned off for parts of the DOM
109
+ * Added the [`hx-disabled`](https://htmx.org/docs#security) attribute to allow htmx to be turned off for parts of the DOM
33
110
  * SSE now uses a full-jitter exponential backoff algorithm on reconnection, using the `htmx.config.wsReconnectDelay`
34
111
  setting
35
112
 
package/README.md CHANGED
@@ -35,7 +35,7 @@ By removing these arbitrary constraints htmx completes HTML as a
35
35
 
36
36
  ```html
37
37
  <!-- Load from unpkg -->
38
- <script src="https://unpkg.com/htmx.org@1.4.1" ></script>
38
+ <script src="https://unpkg.com/htmx.org@1.7.0" ></script>
39
39
  <!-- have a button POST a click via AJAX -->
40
40
  <button hx-post="/clicked" hx-swap="outerHTML">
41
41
  Click Me
@@ -48,6 +48,16 @@ The [`hx-post`](https://htmx.org/attributes/hx-post) and [`hx-swap`](https://htm
48
48
 
49
49
  htmx is the successor to [intercooler.js](http://intercoolerjs.org)
50
50
 
51
+ ### installing as a node package
52
+
53
+ To install using npm:
54
+
55
+ ```
56
+ npm install htmx.org --save
57
+ ```
58
+
59
+ Note there is an old broken package called `htmx`. This is `htmx.org`.
60
+
51
61
  ## website & docs
52
62
 
53
63
  * <https://htmx.org>
@@ -62,6 +72,38 @@ keep the core htmx code tidy
62
72
  * development pull requests should be against the `dev` branch, docs fixes can be made directly against `master`
63
73
  * No time? Then [become a sponsor](https://github.com/sponsors/bigskysoftware#sponsors)
64
74
 
75
+ ### hacking guide
76
+
77
+ To develop htmx locally, you will need to install the development dependencies.
78
+ Use node 15 and run:
79
+
80
+ ```
81
+ npm install
82
+ ```
83
+
84
+ Then, run a web server in the root.
85
+ This is easiest with Python:
86
+
87
+ ```
88
+ python3 -m http.server
89
+ ```
90
+
91
+ You can then run the test suite by navigating to:
92
+
93
+ <http://0.0.0.0:8000/test/>
94
+
95
+ At this point you can modify `/src/htmx.js` to add features, and then add tests in the appropriate area under `/test`.
96
+
97
+ * `/test/index.html` - the root test page from which all other tests are included
98
+ * `/test/attributes` - attribute specific tests
99
+ * `/test/core` - core functionality tests
100
+ * `/test/core/regressions.js` - regression tests
101
+ * `/test/ext` - extension tests
102
+ * `/test/manual` - manual tests that cannot be automated
103
+
104
+ htmx uses the [mocha](https://mochajs.org/) testing framework, the [chai](https://www.chaijs.com/) assertion framework
105
+ and [sinon](https://sinonjs.org/releases/v9/fake-xhr-and-server/) to mock out AJAX requests. They are all OK.
106
+
65
107
  ## haiku
66
108
 
67
109
  *javascript fatigue:<br/>
@@ -0,0 +1,16 @@
1
+ htmx.defineExtension('alpine-morph', {
2
+ isInlineSwap: function (swapStyle) {
3
+ return swapStyle === 'morph';
4
+ },
5
+ handleSwap: function (swapStyle, target, fragment) {
6
+ if (swapStyle === 'morph') {
7
+ if (fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
8
+ Alpine.morph(target, fragment.firstElementChild);
9
+ return [target];
10
+ } else {
11
+ Alpine.morph(target, fragment.outerHTML);
12
+ return [target];
13
+ }
14
+ }
15
+ }
16
+ });
@@ -22,7 +22,7 @@
22
22
  }
23
23
  if (value instanceof Window) return 'Window';
24
24
  return value;
25
- }, ' ');
25
+ });
26
26
  }
27
27
 
28
28
  htmx.defineExtension('event-header', {
@@ -0,0 +1,165 @@
1
+ ;(function () {
2
+ let loadingStatesUndoQueue = []
3
+
4
+ function loadingStateContainer(target) {
5
+ return htmx.closest(target, '[data-loading-states]') || document.body
6
+ }
7
+
8
+ function mayProcessUndoCallback(target, callback) {
9
+ if (document.body.contains(target)) {
10
+ callback()
11
+ }
12
+ }
13
+
14
+ function mayProcessLoadingStateByPath(elt, requestPath) {
15
+ const pathElt = htmx.closest(elt, '[data-loading-path]')
16
+ if (!pathElt) {
17
+ return true
18
+ }
19
+
20
+ return pathElt.getAttribute('data-loading-path') === requestPath
21
+ }
22
+
23
+ function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
24
+ const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
25
+ if (delayElt) {
26
+ const delayInMilliseconds =
27
+ delayElt.getAttribute('data-loading-delay') || 200
28
+ const timeout = setTimeout(() => {
29
+ doCallback()
30
+
31
+ loadingStatesUndoQueue.push(() => {
32
+ mayProcessUndoCallback(targetElt, () => undoCallback())
33
+ })
34
+ }, delayInMilliseconds)
35
+
36
+ loadingStatesUndoQueue.push(() => {
37
+ mayProcessUndoCallback(targetElt, () => clearTimeout(timeout))
38
+ })
39
+ } else {
40
+ doCallback()
41
+ loadingStatesUndoQueue.push(() => {
42
+ mayProcessUndoCallback(targetElt, () => undoCallback())
43
+ })
44
+ }
45
+ }
46
+
47
+ function getLoadingStateElts(loadingScope, type, path) {
48
+ return Array.from(htmx.findAll(loadingScope, `[${type}]`)).filter(
49
+ (elt) => mayProcessLoadingStateByPath(elt, path)
50
+ )
51
+ }
52
+
53
+ function getLoadingTarget(elt) {
54
+ if (elt.getAttribute('data-loading-target')) {
55
+ return Array.from(
56
+ htmx.findAll(elt.getAttribute('data-loading-target'))
57
+ )
58
+ }
59
+ return [elt]
60
+ }
61
+
62
+ htmx.defineExtension('loading-states', {
63
+ onEvent: function (name, evt) {
64
+ if (name === 'htmx:beforeRequest') {
65
+ const container = loadingStateContainer(evt.target)
66
+
67
+ const loadingStateTypes = [
68
+ 'data-loading',
69
+ 'data-loading-class',
70
+ 'data-loading-class-remove',
71
+ 'data-loading-disable',
72
+ ]
73
+
74
+ let loadingStateEltsByType = {}
75
+
76
+ loadingStateTypes.forEach((type) => {
77
+ loadingStateEltsByType[type] = getLoadingStateElts(
78
+ container,
79
+ type,
80
+ evt.detail.pathInfo.path
81
+ )
82
+ })
83
+
84
+ loadingStateEltsByType['data-loading'].forEach((sourceElt) => {
85
+ getLoadingTarget(sourceElt).forEach((targetElt) => {
86
+ queueLoadingState(
87
+ sourceElt,
88
+ targetElt,
89
+ () =>
90
+ (targetElt.style.display =
91
+ sourceElt.getAttribute('data-loading') ||
92
+ 'inline-block'),
93
+ () => (targetElt.style.display = 'none')
94
+ )
95
+ })
96
+ })
97
+
98
+ loadingStateEltsByType['data-loading-class'].forEach(
99
+ (sourceElt) => {
100
+ const classNames = sourceElt
101
+ .getAttribute('data-loading-class')
102
+ .split(' ')
103
+
104
+ getLoadingTarget(sourceElt).forEach((targetElt) => {
105
+ queueLoadingState(
106
+ sourceElt,
107
+ targetElt,
108
+ () =>
109
+ classNames.forEach((className) =>
110
+ targetElt.classList.add(className)
111
+ ),
112
+ () =>
113
+ classNames.forEach((className) =>
114
+ targetElt.classList.remove(className)
115
+ )
116
+ )
117
+ })
118
+ }
119
+ )
120
+
121
+ loadingStateEltsByType['data-loading-class-remove'].forEach(
122
+ (sourceElt) => {
123
+ const classNames = sourceElt
124
+ .getAttribute('data-loading-class-remove')
125
+ .split(' ')
126
+
127
+ getLoadingTarget(sourceElt).forEach((targetElt) => {
128
+ queueLoadingState(
129
+ sourceElt,
130
+ targetElt,
131
+ () =>
132
+ classNames.forEach((className) =>
133
+ targetElt.classList.remove(className)
134
+ ),
135
+ () =>
136
+ classNames.forEach((className) =>
137
+ targetElt.classList.add(className)
138
+ )
139
+ )
140
+ })
141
+ }
142
+ )
143
+
144
+ loadingStateEltsByType['data-loading-disable'].forEach(
145
+ (sourceElt) => {
146
+ getLoadingTarget(sourceElt).forEach((targetElt) => {
147
+ queueLoadingState(
148
+ sourceElt,
149
+ targetElt,
150
+ () => (targetElt.disabled = true),
151
+ () => (targetElt.disabled = false)
152
+ )
153
+ })
154
+ }
155
+ )
156
+ }
157
+
158
+ if (name === 'htmx:afterOnLoad') {
159
+ while (loadingStatesUndoQueue.length > 0) {
160
+ loadingStatesUndoQueue.shift()()
161
+ }
162
+ }
163
+ },
164
+ })
165
+ })()
@@ -5,6 +5,9 @@
5
5
  var _root = this;
6
6
 
7
7
  function dependsOn(pathSpec, url) {
8
+ if (pathSpec === "ignore") {
9
+ return false;
10
+ }
8
11
  var dependencyPath = pathSpec.split("/");
9
12
  var urlPath = url.split("/");
10
13
  for (var i = 0; i < urlPath.length; i++) {
@@ -35,7 +38,7 @@
35
38
  if (name === "htmx:beforeOnLoad") {
36
39
  var config = evt.detail.requestConfig;
37
40
  // mutating call
38
- if (config.verb !== "get") {
41
+ if (config.verb !== "get" && evt.target.getAttribute('path-deps') !== 'ignore') {
39
42
  refreshPath(config.path);
40
43
  }
41
44
  }
@@ -1,7 +1,7 @@
1
1
  // This adds the "preload" extension to htmx. By default, this will
2
2
  // preload the targets of any tags with `href` or `hx-get` attributes
3
3
  // if they also have a `preload` attribute as well. See documentation
4
- // for more detauls
4
+ // for more details
5
5
  htmx.defineExtension("preload", {
6
6
 
7
7
  onEvent: function(name, event) {
@@ -131,7 +131,7 @@ htmx.defineExtension("preload", {
131
131
  init(node)
132
132
 
133
133
  // Initialize all child elements that are anchors or have `hx-get` (use with care)
134
- node.querySelectorAll("a,[hx-get],[data-hx-get").forEach(init)
134
+ node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init)
135
135
  })
136
136
  }
137
137
  })
@@ -15,7 +15,7 @@
15
15
  if (elt.getAttribute) {
16
16
  maybeRemoveMe(elt);
17
17
  if (elt.querySelectorAll) {
18
- var children = elt.querySelectorAll("[remove-me], [data-remove-me");
18
+ var children = elt.querySelectorAll("[remove-me], [data-remove-me]");
19
19
  for (var i = 0; i < children.length; i++) {
20
20
  maybeRemoveMe(children[i]);
21
21
  }
@@ -0,0 +1,15 @@
1
+ htmx.defineExtension('restored', {
2
+ onEvent : function(name, evt) {
3
+ if (name === 'htmx:restored'){
4
+ var restoredElts = evt.detail.document.querySelectorAll(
5
+ "[hx-trigger='restored'],[data-hx-trigger='restored']"
6
+ );
7
+ // need a better way to do this, would prefer to just trigger from evt.detail.elt
8
+ var foundElt = Array.from(restoredElts).find(
9
+ (x) => (x.outerHTML === evt.detail.elt.outerHTML)
10
+ );
11
+ var restoredEvent = evt.detail.triggerEvent(foundElt, 'restored');
12
+ }
13
+ return;
14
+ }
15
+ })