mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 15:25:26 +00:00
Merge pull request #39 from rschroll/multi-trigger
Allow multiple triggers
This commit is contained in:
commit
3555ef1130
108
src/htmx.js
108
src/htmx.js
@ -471,44 +471,46 @@ var htmx = htmx || (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function getTriggerSpec(elt) {
|
||||
function getTriggerSpecs(elt) {
|
||||
|
||||
var triggerSpec = {
|
||||
"trigger" : "click"
|
||||
}
|
||||
var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
|
||||
if (explicitTrigger) {
|
||||
var tokens = splitOnWhitespace(explicitTrigger);
|
||||
if (tokens.length > 0) {
|
||||
var trigger = tokens[0];
|
||||
if (trigger === "every") {
|
||||
triggerSpec.pollInterval = parseInterval(tokens[1]);
|
||||
} else if (trigger.indexOf("sse:") === 0) {
|
||||
triggerSpec.sseEvent = trigger.substr(4);
|
||||
} else {
|
||||
triggerSpec['trigger'] = trigger;
|
||||
for (var i = 1; i < tokens.length; i++) {
|
||||
var token = tokens[i].trim();
|
||||
if (token === "changed") {
|
||||
triggerSpec.changed = true;
|
||||
}
|
||||
if (token === "once") {
|
||||
triggerSpec.once = true;
|
||||
}
|
||||
if (token.indexOf("delay:") === 0) {
|
||||
triggerSpec.delay = parseInterval(token.substr(6));
|
||||
}
|
||||
var triggerSpecs = explicitTrigger.split(',').map(function(triggerString) {
|
||||
var tokens = splitOnWhitespace(triggerString.trim());
|
||||
var trigger = tokens[0]; // splitOnWhitespace returns at least one element
|
||||
if (!trigger)
|
||||
return null;
|
||||
|
||||
if (trigger === "every")
|
||||
return {trigger: 'every', pollInterval: parseInterval(tokens[1])};
|
||||
if (trigger.indexOf("sse:") === 0)
|
||||
return {trigger: 'sse', sseEvent: trigger.substr(4)};
|
||||
|
||||
var triggerSpec = {trigger: trigger};
|
||||
for (var i = 1; i < tokens.length; i++) {
|
||||
var token = tokens[i].trim();
|
||||
if (token === "changed") {
|
||||
triggerSpec.changed = true;
|
||||
}
|
||||
if (token === "once") {
|
||||
triggerSpec.once = true;
|
||||
}
|
||||
if (token.indexOf("delay:") === 0) {
|
||||
triggerSpec.delay = parseInterval(token.substr(6));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (matches(elt, 'form')) {
|
||||
triggerSpec['trigger'] = 'submit';
|
||||
} else if (matches(elt, 'input, textarea, select')) {
|
||||
triggerSpec['trigger'] = 'change';
|
||||
}
|
||||
return triggerSpec;
|
||||
}).filter(x => x !== null);
|
||||
|
||||
if (triggerSpecs.length)
|
||||
return triggerSpecs;
|
||||
}
|
||||
return triggerSpec;
|
||||
|
||||
if (matches(elt, 'form'))
|
||||
return [{trigger: 'submit'}];
|
||||
if (matches(elt, 'input, textarea, select'))
|
||||
return [{trigger: 'change'}];
|
||||
return [{trigger: 'click'}];
|
||||
}
|
||||
|
||||
function parseClassOperation(trimmedValue) {
|
||||
@ -581,7 +583,7 @@ var htmx = htmx || (function () {
|
||||
getRawAttribute(elt,'href').indexOf("#") !== 0;
|
||||
}
|
||||
|
||||
function boostElement(elt, nodeData, triggerSpec) {
|
||||
function boostElement(elt, nodeData, triggerSpecs) {
|
||||
if ((elt.tagName === "A" && isLocalLink(elt)) || elt.tagName === "FORM") {
|
||||
nodeData.boosted = true;
|
||||
var verb, path;
|
||||
@ -593,7 +595,9 @@ var htmx = htmx || (function () {
|
||||
verb = rawAttribute ? rawAttribute.toLowerCase() : "get";
|
||||
path = getRawAttribute(elt, 'action');
|
||||
}
|
||||
addEventListener(elt, verb, path, nodeData, triggerSpec, true);
|
||||
triggerSpecs.forEach(function(triggerSpec) {
|
||||
addEventListener(elt, verb, path, nodeData, triggerSpec, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -723,7 +727,7 @@ var htmx = htmx || (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function processVerbs(elt, nodeData, triggerSpec) {
|
||||
function processVerbs(elt, nodeData, triggerSpecs) {
|
||||
var explicitAction = false;
|
||||
forEach(VERBS, function (verb) {
|
||||
var path = getAttributeValue(elt, 'hx-' + verb);
|
||||
@ -731,19 +735,21 @@ var htmx = htmx || (function () {
|
||||
explicitAction = true;
|
||||
nodeData.path = path;
|
||||
nodeData.verb = verb;
|
||||
if (triggerSpec.sseEvent) {
|
||||
processSSETrigger(elt, verb, path, triggerSpec.sseEvent);
|
||||
} else if (triggerSpec.trigger === "revealed") {
|
||||
initScrollHandler();
|
||||
maybeReveal(elt);
|
||||
} else if (triggerSpec.trigger === "load") {
|
||||
loadImmediately(elt, verb, path, nodeData, triggerSpec.delay);
|
||||
} else if (triggerSpec.pollInterval) {
|
||||
nodeData.polling = true;
|
||||
processPolling(elt, verb, path, triggerSpec.pollInterval);
|
||||
} else {
|
||||
addEventListener(elt, verb, path, nodeData, triggerSpec);
|
||||
}
|
||||
triggerSpecs.forEach(function(triggerSpec) {
|
||||
if (triggerSpec.sseEvent) {
|
||||
processSSETrigger(elt, verb, path, triggerSpec.sseEvent);
|
||||
} else if (triggerSpec.trigger === "revealed") {
|
||||
initScrollHandler();
|
||||
maybeReveal(elt);
|
||||
} else if (triggerSpec.trigger === "load") {
|
||||
loadImmediately(elt, verb, path, nodeData, triggerSpec.delay);
|
||||
} else if (triggerSpec.pollInterval) {
|
||||
nodeData.polling = true;
|
||||
processPolling(elt, verb, path, triggerSpec.pollInterval);
|
||||
} else {
|
||||
addEventListener(elt, verb, path, nodeData, triggerSpec);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return explicitAction;
|
||||
@ -754,11 +760,11 @@ var htmx = htmx || (function () {
|
||||
if (!nodeData.processed) {
|
||||
nodeData.processed = true;
|
||||
|
||||
var triggerSpec = getTriggerSpec(elt);
|
||||
var explicitAction = processVerbs(elt, nodeData, triggerSpec);
|
||||
var triggerSpecs = getTriggerSpecs(elt);
|
||||
var explicitAction = processVerbs(elt, nodeData, triggerSpecs);
|
||||
|
||||
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
|
||||
boostElement(elt, nodeData, triggerSpec);
|
||||
boostElement(elt, nodeData, triggerSpecs);
|
||||
}
|
||||
var sseSrc = getAttributeValue(elt, 'hx-sse-source');
|
||||
if (sseSrc) {
|
||||
|
@ -97,4 +97,62 @@ describe("hx-trigger attribute", function(){
|
||||
form.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('works with multiple events', function()
|
||||
{
|
||||
var requests = 0;
|
||||
this.server.respondWith("GET", "/test", function (xhr) {
|
||||
requests++;
|
||||
xhr.respond(200, {}, "Requests: " + requests);
|
||||
});
|
||||
var div = make('<div hx-trigger="load,click" hx-get="/test">Requests: 0</div>');
|
||||
div.innerHTML.should.equal("Requests: 0");
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Requests: 1");
|
||||
div.click();
|
||||
this.server.respond();
|
||||
div.innerHTML.should.equal("Requests: 2");
|
||||
});
|
||||
|
||||
var specExamples = {
|
||||
"": [{trigger: 'click'}],
|
||||
"every 1s": [{trigger: 'every', pollInterval: 1000}],
|
||||
"sse:/foo": [{trigger: 'sse', sseEvent: '/foo'}],
|
||||
"click": [{trigger: 'click'}],
|
||||
"customEvent": [{trigger: 'customEvent'}],
|
||||
"event changed": [{trigger: 'event', changed: true}],
|
||||
"event once": [{trigger: 'event', once: true}],
|
||||
"event delay:1s": [{trigger: 'event', delay: 1000}],
|
||||
"event changed once delay:1s": [{trigger: 'event', changed: true, once: true, delay: 1000}],
|
||||
"event1,event2": [{trigger: 'event1'}, {trigger: 'event2'}],
|
||||
"event1, event2": [{trigger: 'event1'}, {trigger: 'event2'}],
|
||||
"event1 once, event2 changed": [{trigger: 'event1', once: true}, {trigger: 'event2', changed: true}],
|
||||
"event1,": [{trigger: 'event1'}],
|
||||
",event1": [{trigger: 'event1'}],
|
||||
" ": [{trigger: 'click'}],
|
||||
",": [{trigger: 'click'}]
|
||||
}
|
||||
|
||||
for (const specString in specExamples) {
|
||||
it(`parses "${specString}"`, function()
|
||||
{
|
||||
var div = make(`<div hx-trigger="${specString}"></div>`);
|
||||
var spec = htmx._('getTriggerSpecs')(div);
|
||||
spec.should.deep.equal(specExamples[specString]);
|
||||
});
|
||||
}
|
||||
|
||||
it('sets default trigger for forms', function()
|
||||
{
|
||||
var form = make('<form></form>');
|
||||
var spec = htmx._('getTriggerSpecs')(form);
|
||||
spec.should.deep.equal([{trigger: 'submit'}]);
|
||||
})
|
||||
|
||||
it('sets default trigger for form elements', function()
|
||||
{
|
||||
var form = make('<input></input>');
|
||||
var spec = htmx._('getTriggerSpecs')(form);
|
||||
spec.should.deep.equal([{trigger: 'change'}]);
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -8,9 +8,10 @@ title: </> htmx - hx-trigger
|
||||
The `hx-trigger` attribute allows you to specify what triggers an AJAX request. A trigger
|
||||
value can be one of the following:
|
||||
|
||||
* An event name (e.g. "click") followed by a set of event modifiers
|
||||
* An event name (e.g. "click" or "my-custom-event") followed by a set of event modifiers
|
||||
* A polling definition of the form `every <timing declaration>`
|
||||
* An SSE event declaration of the form `sse:<event name>`
|
||||
* A comma-separated list of such events
|
||||
|
||||
### Standard Events
|
||||
|
||||
@ -74,6 +75,14 @@ Here is an example:
|
||||
This example establishes an SSE connection to the `event_stream` end point which then triggers
|
||||
a `GET` to the `/chatroom` url whenever the `chatter` event is seen.
|
||||
|
||||
### Multiple Triggers
|
||||
|
||||
Multiple triggers can be provided, seprarated by commas. Each trigger gets its own options.
|
||||
```html
|
||||
<div hx-get="/news" hx-trigger="load, click delay:1s"></div>
|
||||
```
|
||||
This example will load `/news` immediate on the page load, and then again with a delay of one second after each click.
|
||||
|
||||
### Notes
|
||||
|
||||
* `hx-trigger` is not inherited
|
||||
* `hx-trigger` is not inherited
|
||||
|
@ -171,6 +171,8 @@ You can use these two attributes to implement a common UX pattern, [Active Searc
|
||||
This input will issue a request 500 milliseconds after a key up event if the input has been changed and inserts the results
|
||||
into the `div` with the id `search-results`.
|
||||
|
||||
Multiple triggers can be specified in the [hx-trigger](/attributes/hx-trigger) attribute, separated by commas.
|
||||
|
||||
#### <a name="special-events"></a> [Special Events](#special-events)
|
||||
|
||||
htmx provides a few special events for use in [hx-trigger](/attributes/hx-trigger):
|
||||
|
Loading…
x
Reference in New Issue
Block a user