diff --git a/src/htmx.js b/src/htmx.js index 37ad22698..047cf180c 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -700,9 +700,10 @@ var htmx = (function() { * @property {ListenerInfo[]} [listenerInfos] * @property {boolean} [cancelled] * @property {boolean} [triggeredOnce] - * @property {number} [delayed] + * @property {WeakMap} [delayed] * @property {number|null} [throttle] * @property {WeakMap>} [lastValue] + * @property {WeakMap>} [pendingValue] * @property {boolean} [loaded] * @property {string} [path] * @property {string} [verb] @@ -2503,6 +2504,14 @@ var htmx = (function() { } else { eltsToListenOn = [elt] } + if (triggerSpec.delay > 0) { + if (!('delayed' in elementData)) { + elementData.delayed = new WeakMap() + } + if (!('pendingValue' in elementData)) { + elementData.pendingValue = new WeakMap() + } + } // store the initial values of the elements, so we can tell if they change if (triggerSpec.changed) { if (!('lastValue' in elementData)) { @@ -2559,13 +2568,22 @@ var htmx = (function() { // @ts-ignore value will be undefined for non-input elements, which is fine const value = node.value const lastValue = elementData.lastValue.get(triggerSpec) - if (lastValue.has(node) && lastValue.get(node) === value) { - return + if (triggerSpec.delay > 0) { + if (!elementData.pendingValue.has(triggerSpec)) { + elementData.pendingValue.set(triggerSpec, new Map()) + } + const pendingValue = elementData.pendingValue.get(triggerSpec) + pendingValue.set(node, value) + } else { + if (lastValue.has(node) && lastValue.get(node) === value) { + return + } + lastValue.set(node, value) } - lastValue.set(node, value) } - if (elementData.delayed) { - clearTimeout(elementData.delayed) + if (elementData.delayed && elementData.delayed.has(triggerSpec)) { + clearTimeout(elementData.delayed.get(triggerSpec)) + elementData.delayed.delete(triggerSpec) } if (elementData.throttle) { return @@ -2580,10 +2598,27 @@ var htmx = (function() { }, triggerSpec.throttle) } } else if (triggerSpec.delay > 0) { - elementData.delayed = getWindow().setTimeout(function() { + elementData.delayed.set(triggerSpec, getWindow().setTimeout(function() { + elementData.delayed.delete(triggerSpec) + if (triggerSpec.changed) { + const lastValue = elementData.lastValue.get(triggerSpec) || new WeakMap() + const pendingValue = elementData.pendingValue.get(triggerSpec) || new Map() + elementData.pendingValue.set(triggerSpec, new Map()) + + let changed = false + pendingValue.forEach((new_value, new_node) => { + if (lastValue.get(new_node) !== new_value) { + changed = true + lastValue.set(new_node, new_value) + } + }) + if (!changed) { + return + } + } triggerEvent(elt, 'htmx:trigger') handler(elt, evt) - }, triggerSpec.delay) + }, triggerSpec.delay)) } else { triggerEvent(elt, 'htmx:trigger') handler(elt, evt) diff --git a/test/attributes/hx-trigger.js b/test/attributes/hx-trigger.js index 8141ee2f9..87a379800 100644 --- a/test/attributes/hx-trigger.js +++ b/test/attributes/hx-trigger.js @@ -871,6 +871,60 @@ describe('hx-trigger attribute', function() { }, 50) }) + it('two delays on the same node are independent', function(done) { + var requests = 0 + var server = this.server + this.server.respondWith('GET', '/test', function(xhr) { + requests++ + xhr.respond(200, {}, 'Requests: ' + requests) + }) + this.server.respondWith('GET', '/bar', 'bar') + var button = make('') + var div = make("
") + + div.click() + button.click() + this.server.respond() + div.innerText.should.equal('') + + setTimeout(function() { + server.respond() + div.innerText.should.equal('Requests: 1') + + setTimeout(function() { + server.respond() + div.innerText.should.equal('Requests: 2') + + done() + }, 50) + }, 20) + }) + + it('delay for multiple nodes are shared', function(done) { + var requests = 0 + var server = this.server + this.server.respondWith('GET', '/test', function(xhr) { + requests++ + xhr.respond(200, {}, 'Requests: ' + requests) + }) + this.server.respondWith('GET', '/bar', 'bar') + var button1 = make('') + var button2 = make('') + var div = make("
") + + button1.click() + button2.click() + this.server.respond() + div.innerText.should.equal('') + + setTimeout(function() { + server.respond() + div.innerText.should.equal('Requests: 1') + + done() + }, 20) + }) + it('A 0 delay does not delay the request', function(done) { var requests = 0 this.server.respondWith('GET', '/test', function(xhr) { diff --git a/www/content/docs.md b/www/content/docs.md index 3b0d22d7e..4e10f13b6 100644 --- a/www/content/docs.md +++ b/www/content/docs.md @@ -245,6 +245,8 @@ Other modifiers you can use for triggers are: * `changed` - only issue a request if the value of the element has changed * `delay: