mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-28 05:21:18 +00:00
add hx-disabled-elts
functionality
This commit is contained in:
parent
77bdc2c1da
commit
af0dc9c352
31
src/htmx.js
31
src/htmx.js
@ -2343,7 +2343,20 @@ return (function () {
|
||||
return indicators;
|
||||
}
|
||||
|
||||
function removeRequestIndicatorClasses(indicators) {
|
||||
function disableElements(elt) {
|
||||
var disabledElts = findAttributeTargets(elt, 'hx-disabled-elts');
|
||||
if (disabledElts == null) {
|
||||
disabledElts = [];
|
||||
}
|
||||
forEach(disabledElts, function (disabledElement) {
|
||||
var internalData = getInternalData(disabledElement);
|
||||
internalData.requestCount = (internalData.requestCount || 0) + 1;
|
||||
disabledElement.setAttribute("disabled", "");
|
||||
});
|
||||
return disabledElts;
|
||||
}
|
||||
|
||||
function removeRequestIndicators(indicators, disabled) {
|
||||
forEach(indicators, function (ic) {
|
||||
var internalData = getInternalData(ic);
|
||||
internalData.requestCount = (internalData.requestCount || 0) - 1;
|
||||
@ -2351,6 +2364,13 @@ return (function () {
|
||||
ic.classList["remove"].call(ic.classList, htmx.config.requestClass);
|
||||
}
|
||||
});
|
||||
forEach(disabled, function (disabledElement) {
|
||||
var internalData = getInternalData(disabledElement);
|
||||
internalData.requestCount = (internalData.requestCount || 0) - 1;
|
||||
if (internalData.requestCount === 0) {
|
||||
disabledElement.removeAttribute('disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
@ -3186,7 +3206,7 @@ return (function () {
|
||||
var hierarchy = hierarchyForElt(elt);
|
||||
responseInfo.pathInfo.responsePath = getPathFromResponse(xhr);
|
||||
responseHandler(elt, responseInfo);
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
removeRequestIndicators(indicators, disableElts);
|
||||
triggerEvent(elt, 'htmx:afterRequest', responseInfo);
|
||||
triggerEvent(elt, 'htmx:afterOnLoad', responseInfo);
|
||||
// if the body no longer contains the element, trigger the event on the closest parent
|
||||
@ -3212,21 +3232,21 @@ return (function () {
|
||||
}
|
||||
}
|
||||
xhr.onerror = function () {
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
removeRequestIndicators(indicators, disableElts);
|
||||
triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
|
||||
triggerErrorEvent(elt, 'htmx:sendError', responseInfo);
|
||||
maybeCall(reject);
|
||||
endRequestLock();
|
||||
}
|
||||
xhr.onabort = function() {
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
removeRequestIndicators(indicators, disableElts);
|
||||
triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
|
||||
triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo);
|
||||
maybeCall(reject);
|
||||
endRequestLock();
|
||||
}
|
||||
xhr.ontimeout = function() {
|
||||
removeRequestIndicatorClasses(indicators);
|
||||
removeRequestIndicators(indicators, disableElts);
|
||||
triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
|
||||
triggerErrorEvent(elt, 'htmx:timeout', responseInfo);
|
||||
maybeCall(reject);
|
||||
@ -3238,6 +3258,7 @@ return (function () {
|
||||
return promise
|
||||
}
|
||||
var indicators = addRequestIndicatorClasses(elt);
|
||||
var disableElts = disableElements(elt);
|
||||
|
||||
forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
|
||||
forEach([xhr, xhr.upload], function (target) {
|
||||
|
94
test/attributes/hx-disabled-elts.js
Normal file
94
test/attributes/hx-disabled-elts.js
Normal file
@ -0,0 +1,94 @@
|
||||
describe("hx-disabled-elts attribute", function(){
|
||||
beforeEach(function() {
|
||||
this.server = sinon.fakeServer.create();
|
||||
clearWorkArea();
|
||||
});
|
||||
afterEach(function() {
|
||||
this.server.restore();
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
it('single element can be disabled w/ hx-disabled elts', function()
|
||||
{
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var btn = make('<button hx-get="/test" hx-disabled-elts="this">Click Me!</button>')
|
||||
btn.hasAttribute('disabled').should.equal(false);
|
||||
btn.click();
|
||||
btn.hasAttribute('disabled').should.equal(true);
|
||||
this.server.respond();
|
||||
btn.hasAttribute('disabled').should.equal(false);
|
||||
});
|
||||
|
||||
|
||||
it('single element can be disabled w/ data-hx-disabled elts', function()
|
||||
{
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var btn = make('<button hx-get="/test" data-hx-disabled-elts="this">Click Me!</button>')
|
||||
btn.hasAttribute('disabled').should.equal(false);
|
||||
btn.click();
|
||||
btn.hasAttribute('disabled').should.equal(true);
|
||||
this.server.respond();
|
||||
btn.hasAttribute('disabled').should.equal(false);
|
||||
});
|
||||
|
||||
it('single element can be disabled w/ closest syntax', function()
|
||||
{
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var fieldset = make('<fieldset><button id="b1" hx-get="/test" hx-disabled-elts="closest fieldset">Click Me!</button></fieldset>')
|
||||
var btn = byId('b1');
|
||||
fieldset.hasAttribute('disabled').should.equal(false);
|
||||
btn.click();
|
||||
fieldset.hasAttribute('disabled').should.equal(true);
|
||||
this.server.respond();
|
||||
fieldset.hasAttribute('disabled').should.equal(false);
|
||||
});
|
||||
|
||||
it('multiple requests with same disabled elt are handled properly', function()
|
||||
{
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var b1 = make('<button hx-get="/test" hx-disabled-elts="#b3">Click Me!</button>')
|
||||
var b2 = make('<button hx-get="/test" hx-disabled-elts="#b3">Click Me!</button>')
|
||||
var b3 = make('<button id="b3">Demo</button>')
|
||||
b3.hasAttribute('disabled').should.equal(false);
|
||||
|
||||
b1.click();
|
||||
b3.hasAttribute('disabled').should.equal(true);
|
||||
|
||||
b2.click();
|
||||
b3.hasAttribute('disabled').should.equal(true);
|
||||
|
||||
|
||||
// hack to make sinon process only one response
|
||||
this.server.processRequest(this.server.queue.shift());
|
||||
|
||||
b3.hasAttribute('disabled').should.equal(true);
|
||||
|
||||
this.server.respond();
|
||||
|
||||
b3.hasAttribute('disabled').should.equal(false);
|
||||
|
||||
});
|
||||
|
||||
it('multiple elts can be disabled', function()
|
||||
{
|
||||
this.server.respondWith("GET", "/test", "Clicked!");
|
||||
var b1 = make('<button hx-get="/test" hx-disabled-elts="#b2, #b3">Click Me!</button>')
|
||||
var b2 = make('<button id="b2">Click Me!</button>')
|
||||
var b3 = make('<button id="b3">Demo</button>')
|
||||
|
||||
b2.hasAttribute('disabled').should.equal(false);
|
||||
b3.hasAttribute('disabled').should.equal(false);
|
||||
|
||||
b1.click();
|
||||
b2.hasAttribute('disabled').should.equal(true);
|
||||
b3.hasAttribute('disabled').should.equal(true);
|
||||
|
||||
this.server.respond();
|
||||
|
||||
b2.hasAttribute('disabled').should.equal(false);
|
||||
b3.hasAttribute('disabled').should.equal(false);
|
||||
|
||||
});
|
||||
|
||||
|
||||
})
|
@ -72,6 +72,7 @@
|
||||
<script src="attributes/hx-history.js"></script>
|
||||
<script src="attributes/hx-include.js"></script>
|
||||
<script src="attributes/hx-indicator.js"></script>
|
||||
<script src="attributes/hx-disabled-elts.js"></script>
|
||||
<script src="attributes/hx-disinherit.js"></script>
|
||||
<script src="attributes/hx-on.js"></script>
|
||||
<script src="attributes/hx-on-wildcard.js"></script>
|
||||
|
@ -68,6 +68,10 @@ Autorespond: <input id="autorespond" type="checkbox" onclick="toggleAutoRespond(
|
||||
|
||||
<button onclick="console.log(this, event)">Log It...</button>
|
||||
|
||||
<button hx-get="/whatever">
|
||||
|
||||
</button>
|
||||
|
||||
<script>
|
||||
let requestCount = 0;
|
||||
this.server.respondWith("GET", "/demo", function(xhr){
|
||||
|
26
www/content/attributes/hx-disabled-elts.md
Normal file
26
www/content/attributes/hx-disabled-elts.md
Normal file
@ -0,0 +1,26 @@
|
||||
+++
|
||||
title = "hx-disabled-elts"
|
||||
+++
|
||||
|
||||
The `hx-disabled-elts` 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 is a CSS query selector of the element or elements to apply the class to,
|
||||
or the keyword [`closest`](https://developer.mozilla.org/docs/Web/API/Element/closest), followed by a CSS selector,
|
||||
which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. `closest tr`), or
|
||||
the keyword `this`
|
||||
|
||||
Here is an example with a button that will disable itself during a request:
|
||||
|
||||
```html
|
||||
<button hx-post="/example" hx-disabled-elts="this">
|
||||
Post It!
|
||||
</button>
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Notes
|
||||
|
||||
* `hx-disable-elts` is inherited and can be placed on a parent element
|
@ -368,6 +368,9 @@ attribute with a CSS selector to do so:
|
||||
Here we call out the indicator explicitly by id. Note that we could have placed the class on the parent `div` as well
|
||||
and had the same effect.
|
||||
|
||||
You can also add the [the `disabled` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled) to
|
||||
elements for the duration of a request by using the [hx-disabled-elts](@/attributes/hx-indicator.md) attribute.
|
||||
|
||||
### Targets
|
||||
|
||||
If you want the response to be loaded into a different element other than the one that made the request, you can
|
||||
|
@ -19,20 +19,20 @@ The following are the most common attributes when using htmx.
|
||||
|
||||
<div class="info-table">
|
||||
|
||||
| Attribute | Description |
|
||||
|----------------------------------------------------|-------------|
|
||||
| [`hx-boost`](@/attributes/hx-boost.md) | add or remove [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement) for links and forms
|
||||
| [`hx-get`](@/attributes/hx-get.md) | issues a `GET` to the specified URL
|
||||
| [`hx-post`](@/attributes/hx-post.md) | issues a `POST` to the specified URL
|
||||
| [`hx-on`](@/attributes/hx-on.md) | handle any event with a script inline
|
||||
| [`hx-push-url`](@/attributes/hx-push-url.md) | pushes the URL into the browser location bar, creating a new history entry
|
||||
| [`hx-select`](@/attributes/hx-select.md) | select content to swap in from a response
|
||||
| [`hx-select-oob`](@/attributes/hx-select-oob.md) | select content to swap in from a response, out of band (somewhere other than the target)
|
||||
| [`hx-swap`](@/attributes/hx-swap.md) | controls how content is swapped in (`outerHTML`, `beforeend`, `afterend`, ...)
|
||||
| [`hx-swap-oob`](@/attributes/hx-swap-oob.md) | marks content in a response to be out of band (should swap in somewhere other than the target)
|
||||
| [`hx-target`](@/attributes/hx-target.md) | specifies the target element to be swapped
|
||||
| [`hx-trigger`](@/attributes/hx-trigger.md) | specifies the event that triggers the request
|
||||
| [`hx-vals`](@/attributes/hx-vals.md) | adds values to the parameters to submit with the request (JSON-formatted)
|
||||
| Attribute | Description |
|
||||
|--------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| [`hx-boost`](@/attributes/hx-boost.md) | add or remove [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement) for links and forms |
|
||||
| [`hx-get`](@/attributes/hx-get.md) | issues a `GET` to the specified URL |
|
||||
| [`hx-post`](@/attributes/hx-post.md) | issues a `POST` to the specified URL |
|
||||
| [`hx-on`](@/attributes/hx-on.md) | handle any event with a script inline |
|
||||
| [`hx-push-url`](@/attributes/hx-push-url.md) | pushes the URL into the browser location bar, creating a new history entry |
|
||||
| [`hx-select`](@/attributes/hx-select.md) | select content to swap in from a response |
|
||||
| [`hx-select-oob`](@/attributes/hx-select-oob.md) | select content to swap in from a response, out of band (somewhere other than the target) |
|
||||
| [`hx-swap`](@/attributes/hx-swap.md) | controls how content is swapped in (`outerHTML`, `beforeend`, `afterend`, ...) |
|
||||
| [`hx-swap-oob`](@/attributes/hx-swap-oob.md) | marks content in a response to be out of band (should swap in somewhere other than the target) |
|
||||
| [`hx-target`](@/attributes/hx-target.md) | specifies the target element to be swapped |
|
||||
| [`hx-trigger`](@/attributes/hx-trigger.md) | specifies the event that triggers the request |
|
||||
| [`hx-vals`](@/attributes/hx-vals.md) | adds values to the parameters to submit with the request (JSON-formatted) |
|
||||
|
||||
</div>
|
||||
|
||||
@ -42,32 +42,32 @@ The table below lists all other attributes available in htmx.
|
||||
|
||||
<div class="info-table">
|
||||
|
||||
| Attribute | Description |
|
||||
|----------------------------------------------------|-------------|
|
||||
| [`hx-confirm`](@/attributes/hx-confirm.md) | shows a `confirm()` dialog before issuing a request
|
||||
| [`hx-delete`](@/attributes/hx-delete.md) | issues a `DELETE` to the specified URL
|
||||
| [`hx-disable`](@/attributes/hx-disable.md) | disables htmx processing for the given node and any children nodes
|
||||
| [`hx-disinherit`](@/attributes/hx-disinherit.md) | control and disable automatic attribute inheritance for child nodes
|
||||
| [`hx-encoding`](@/attributes/hx-encoding.md) | changes the request encoding type
|
||||
| [`hx-ext`](@/attributes/hx-ext.md) | extensions to use for this element
|
||||
| [`hx-headers`](@/attributes/hx-headers.md) | adds to the headers that will be submitted with the request
|
||||
| [`hx-history`](@/attributes/hx-history.md) | prevent sensitive data being saved to the history cache
|
||||
| [`hx-history-elt`](@/attributes/hx-history-elt.md) | the element to snapshot and restore during history navigation
|
||||
| [`hx-include`](@/attributes/hx-include.md) | include additional data in requests
|
||||
| [`hx-indicator`](@/attributes/hx-indicator.md) | the element to put the `htmx-request` class on during the request
|
||||
| [`hx-params`](@/attributes/hx-params.md) | filters the parameters that will be submitted with a request
|
||||
| [`hx-patch`](@/attributes/hx-patch.md) | issues a `PATCH` to the specified URL
|
||||
| [`hx-preserve`](@/attributes/hx-preserve.md) | specifies elements to keep unchanged between requests
|
||||
| [`hx-prompt`](@/attributes/hx-prompt.md) | shows a `prompt()` before submitting a request
|
||||
| [`hx-put`](@/attributes/hx-put.md) | issues a `PUT` to the specified URL
|
||||
| [`hx-replace-url`](@/attributes/hx-replace-url.md) | replace the URL in the browser location bar
|
||||
| [`hx-request`](@/attributes/hx-request.md) | configures various aspects of the request
|
||||
| [`hx-sse`](@/extensions/server-sent-events.md) | has been moved to an extension. [Documentation for older versions](@/attributes/hx-sse.md)
|
||||
| [`hx-sync`](@/attributes/hx-sync.md) | control how requests made by different elements are synchronized
|
||||
| [`hx-validate`](@/attributes/hx-validate.md) | force elements to validate themselves before a request
|
||||
| [`hx-vars`](@/attributes/hx-vars.md) | adds values dynamically to the parameters to submit with the request (deprecated, please use [`hx-vals`](@/attributes/hx-vals.md))
|
||||
| [`hx-ws`](@/extensions/web-sockets.md) | has been moved to an extension. [Documentation for older versions](@/attributes/hx-ws.md)
|
||||
|
||||
| Attribute | Description |
|
||||
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`hx-confirm`](@/attributes/hx-confirm.md) | shows a `confirm()` dialog before issuing a request |
|
||||
| [`hx-delete`](@/attributes/hx-delete.md) | issues a `DELETE` to the specified URL |
|
||||
| [`hx-disable`](@/attributes/hx-disable.md) | disables htmx processing for the given node and any children nodes |
|
||||
| [`hx-disabled-elts`](@/attributes/hx-disabled-elts.md) | adds the `disabled` attribute to the specified elements while a request is in flight |
|
||||
| [`hx-disinherit`](@/attributes/hx-disinherit.md) | control and disable automatic attribute inheritance for child nodes |
|
||||
| [`hx-encoding`](@/attributes/hx-encoding.md) | changes the request encoding type |
|
||||
| [`hx-ext`](@/attributes/hx-ext.md) | extensions to use for this element |
|
||||
| [`hx-headers`](@/attributes/hx-headers.md) | adds to the headers that will be submitted with the request |
|
||||
| [`hx-history`](@/attributes/hx-history.md) | prevent sensitive data being saved to the history cache |
|
||||
| [`hx-history-elt`](@/attributes/hx-history-elt.md) | the element to snapshot and restore during history navigation |
|
||||
| [`hx-include`](@/attributes/hx-include.md) | include additional data in requests |
|
||||
| [`hx-indicator`](@/attributes/hx-indicator.md) | the element to put the `htmx-request` class on during the request |
|
||||
| [`hx-params`](@/attributes/hx-params.md) | filters the parameters that will be submitted with a request |
|
||||
| [`hx-patch`](@/attributes/hx-patch.md) | issues a `PATCH` to the specified URL |
|
||||
| [`hx-preserve`](@/attributes/hx-preserve.md) | specifies elements to keep unchanged between requests |
|
||||
| [`hx-prompt`](@/attributes/hx-prompt.md) | shows a `prompt()` before submitting a request |
|
||||
| [`hx-put`](@/attributes/hx-put.md) | issues a `PUT` to the specified URL |
|
||||
| [`hx-replace-url`](@/attributes/hx-replace-url.md) | replace the URL in the browser location bar |
|
||||
| [`hx-request`](@/attributes/hx-request.md) | configures various aspects of the request |
|
||||
| [`hx-sse`](@/extensions/server-sent-events.md) | has been moved to an extension. [Documentation for older versions](@/attributes/hx-sse.md) |
|
||||
| [`hx-sync`](@/attributes/hx-sync.md) | control how requests made by different elements are synchronized |
|
||||
| [`hx-validate`](@/attributes/hx-validate.md) | force elements to validate themselves before a request |
|
||||
| [`hx-vars`](@/attributes/hx-vars.md) | adds values dynamically to the parameters to submit with the request (deprecated, please use [`hx-vals`](@/attributes/hx-vals.md)) |
|
||||
| [`hx-ws`](@/extensions/web-sockets.md) | has been moved to an extension. [Documentation for older versions](@/attributes/hx-ws.md) |
|
||||
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user