diff --git a/src/htmx.js b/src/htmx.js
index 74bf5a79..62afd334 100644
--- a/src/htmx.js
+++ b/src/htmx.js
@@ -2975,12 +2975,13 @@ return (function () {
}
}
+ var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
// allow event-based confirmation w/ a callback
- if (!confirmed) {
- var issueRequest = function() {
- return issueAjaxRequest(verb, path, elt, event, etc, true);
+ if (confirmed === undefined) {
+ var issueRequest = function(skipConfirmation) {
+ return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation);
}
- var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest};
+ var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion};
if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) {
maybeCall(resolve);
return promise;
@@ -3081,8 +3082,7 @@ return (function () {
}
}
- var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
- if (confirmQuestion) {
+ if (confirmQuestion && !confirmed) {
if(!confirm(confirmQuestion)) {
maybeCall(resolve);
endRequestLock()
diff --git a/test/attributes/hx-confirm.js b/test/attributes/hx-confirm.js
new file mode 100644
index 00000000..7f8516d7
--- /dev/null
+++ b/test/attributes/hx-confirm.js
@@ -0,0 +1,126 @@
+describe("hx-confirm attribute", function () {
+ var confirm
+ beforeEach(function () {
+ this.server = makeServer();
+ confirm = sinon.stub(window, "confirm");
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ confirm.restore()
+ clearWorkArea();
+ });
+
+ it('prompts using window.confirm when hx-confirm is set', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ confirm.returns(true);
+ var btn = make('')
+ btn.click();
+ confirm.calledOnce.should.equal(true);
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ })
+
+ it('stops the request if confirm is cancelled', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ confirm.returns(false);
+ var btn = make('')
+ btn.click();
+ confirm.calledOnce.should.equal(true);
+ this.server.respond();
+ btn.innerHTML.should.equal("Click Me!");
+ })
+
+ it('uses the value of hx-confirm as the prompt', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ confirm.returns(false);
+ var btn = make('')
+ btn.click();
+ confirm.firstCall.args[0].should.equal("Sure?");
+ this.server.respond();
+ btn.innerHTML.should.equal("Click Me!");
+ })
+
+ it('should prompt when htmx:confirm handler calls issueRequest', function () {
+ try {
+ var btn = make('')
+ var handler = htmx.on("htmx:confirm", function (evt) {
+ evt.preventDefault();
+ evt.detail.issueRequest();
+ });
+ btn.click();
+ confirm.calledOnce.should.equal(true);
+ } finally {
+ htmx.off("htmx:confirm", handler);
+ }
+ })
+
+ it('should include the question in htmx:confirm event', function () {
+ var stub = sinon.stub();
+ try {
+ var btn = make('')
+ var handler = htmx.on("htmx:confirm", stub);
+ btn.click();
+ stub.calledOnce.should.equal(true);
+ stub.firstCall.args[0].detail.should.have.property("question", "Surely?");
+ } finally {
+ htmx.off("htmx:confirm", handler);
+ }
+ })
+
+ it('should allow skipping built-in window.confirm when using issueRequest', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ try {
+ var btn = make('')
+ var handler = htmx.on("htmx:confirm", function (evt) {
+ evt.detail.question.should.equal("Sure?");
+ evt.preventDefault();
+ evt.detail.issueRequest(true);
+ });
+ btn.click();
+ confirm.called.should.equal(false);
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ } finally {
+ htmx.off("htmx:confirm", handler);
+ }
+ })
+ it('should allow skipping built-in window.confirm when using issueRequest', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ try {
+ var btn = make('')
+ var handler = htmx.on("htmx:confirm", function (evt) {
+ evt.detail.question.should.equal("Sure?");
+ evt.preventDefault();
+ evt.detail.issueRequest(true);
+ });
+ btn.click();
+ confirm.called.should.equal(false);
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ } finally {
+ htmx.off("htmx:confirm", handler);
+ }
+ })
+
+
+ it('should allow htmx:confirm even when no hx-confirm is set', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ try {
+ var btn = make('')
+ var handler = htmx.on("htmx:confirm", function (evt) {
+ evt.detail.should.have.property("question", null);
+ evt.preventDefault();
+ evt.detail.issueRequest();
+ });
+ btn.click();
+ confirm.called.should.equal(false); // no hx-confirm means no window.confirm
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ } finally {
+ htmx.off("htmx:confirm", handler);
+ }
+ })
+
+
+});
\ No newline at end of file
diff --git a/test/core/events.js b/test/core/events.js
index 261a407f..7c1c1596 100644
--- a/test/core/events.js
+++ b/test/core/events.js
@@ -659,7 +659,7 @@ describe("Core htmx Events", function() {
this.server.respond();
div.innerHTML.should.equal("updated");
} finally {
- htmx.off("htmx:load", handler);
+ htmx.off("htmx:confirm", handler);
}
});
diff --git a/test/index.html b/test/index.html
index 50892993..b0841d25 100644
--- a/test/index.html
+++ b/test/index.html
@@ -70,6 +70,7 @@
+
diff --git a/www/content/attributes/hx-confirm.md b/www/content/attributes/hx-confirm.md
index 5a8ceaa5..9cb3ced4 100644
--- a/www/content/attributes/hx-confirm.md
+++ b/www/content/attributes/hx-confirm.md
@@ -13,6 +13,16 @@ Here is an example:
```
+## Event details
+
+The event triggered by `hx-confirm` contains additional properties in its `detail`:
+
+* triggeringEvent: the event that triggered the original request
+* issueRequest(skipConfirmation=false): a callback which can be used to confirm the AJAX request
+* question: the value of the `hx-confirm` attribute on the HTML element
+
## Notes
* `hx-confirm` is inherited and can be placed on a parent element
+* `hx-confirm` uses the browser's `window.confirm` by default. You can customize this behavior as shown [in this example](@/examples/confirm.md).
+* a boolean `skipConfirmation` can be passed to the `issueRequest` callback; if true (defaults to false), the `window.confirm` will not be called and the AJAX request is issued directly
diff --git a/www/content/examples/confirm.md b/www/content/examples/confirm.md
index fde399f0..d1f00c49 100644
--- a/www/content/examples/confirm.md
+++ b/www/content/examples/confirm.md
@@ -8,32 +8,78 @@ action. This uses the default `confirm()` function in javascript which, while t
applications UX.
In this example we will see how to use [sweetalert2](https://sweetalert2.github.io) and the [`htmx:confirm`](@/events.md#htmx:confirm)
-event to implement a custom confirmation dialog.
+event to implement a custom confirmation dialog. Below are two examples, one with `hyperscript` using a click+custom event method, and one in vanilla JS and the built-in `hx-confirm` attribute.
+
+## Hyperscript, on click+custom event
```html
-