mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-27 13:01:03 +00:00
hx-inherit tests x documentation
This commit is contained in:
parent
fb78c826d1
commit
d025afe49f
@ -90,7 +90,7 @@ you can then run the test suite by navigating to:
|
||||
at this point you can modify `/src/htmx.js` to add features, and then add tests in the appropriate area under `/test`
|
||||
|
||||
* `/test/index.html` - the root test page from which all other tests are included
|
||||
* `/test/attributres` - attribute specific tests
|
||||
* `/test/attributes` - attribute specific tests
|
||||
* `/test/core` - core functionality tests
|
||||
* `/test/core/regressions.js` - regresssion tests
|
||||
* `/test/ext` - extension tests
|
||||
|
49
src/htmx.js
49
src/htmx.js
@ -79,17 +79,17 @@ return (function () {
|
||||
// Utilities
|
||||
//====================================================================
|
||||
|
||||
function parseInterval(str) {
|
||||
if (str == undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (str.slice(-2) == "ms") {
|
||||
return parseFloat(str.slice(0,-2)) || undefined
|
||||
}
|
||||
if (str.slice(-1) == "s") {
|
||||
return (parseFloat(str.slice(0,-1)) * 1000) || undefined
|
||||
}
|
||||
return parseFloat(str) || undefined
|
||||
function parseInterval(str) {
|
||||
if (str == undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (str.slice(-2) == "ms") {
|
||||
return parseFloat(str.slice(0,-2)) || undefined
|
||||
}
|
||||
if (str.slice(-1) == "s") {
|
||||
return (parseFloat(str.slice(0,-1)) * 1000) || undefined
|
||||
}
|
||||
return parseFloat(str) || undefined
|
||||
}
|
||||
|
||||
function getRawAttribute(elt, name) {
|
||||
@ -106,6 +106,15 @@ return (function () {
|
||||
return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, "data-" + qualifiedName);
|
||||
}
|
||||
|
||||
function getAttributeInheritance(elt, closestMatch, attributeName) {
|
||||
var data = getInternalData(elt);
|
||||
if (elt === closestMatch || attributeName === "hx-boost" || data.boosted) return
|
||||
var inheritAttr = getAttributeValue(closestMatch, "hx-inherit");
|
||||
if (inheritAttr && (inheritAttr === "false" || inheritAttr.includes(attributeName))) {
|
||||
return "unset";
|
||||
}
|
||||
}
|
||||
|
||||
function parentElt(elt) {
|
||||
return elt.parentElement;
|
||||
}
|
||||
@ -127,14 +136,8 @@ return (function () {
|
||||
function getClosestAttributeValue(elt, attributeName) {
|
||||
var closestAttr = null;
|
||||
getClosestMatch(elt, function (e) {
|
||||
closestAttr = getAttributeValue(e, attributeName)
|
||||
if (closestAttr && elt !== e) {
|
||||
var inheritAttr = getAttributeValue(e, "hx-inherit");
|
||||
if (inheritAttr && (inheritAttr === "false" || inheritAttr.includes(attributeName))) {
|
||||
closestAttr = "unset";
|
||||
}
|
||||
}
|
||||
return closestAttr;
|
||||
closestAttr = getAttributeValue(e, attributeName);
|
||||
return closestAttr = getAttributeInheritance(elt, e, attributeName) || closestAttr;
|
||||
});
|
||||
if (closestAttr !== "unset") {
|
||||
return closestAttr;
|
||||
@ -383,7 +386,7 @@ return (function () {
|
||||
}
|
||||
|
||||
function querySelectorAllExt(elt, selector) {
|
||||
if (selector.indexOf("closest ") === 0) {
|
||||
if (selector.indexOf("closest ") === 0) {
|
||||
return [closest(elt, selector.substr(8))];
|
||||
} else if (selector.indexOf("find ") === 0) {
|
||||
return [find(elt, selector.substr(5))];
|
||||
@ -452,7 +455,7 @@ return (function () {
|
||||
|
||||
function getTarget(elt) {
|
||||
var explicitTarget = getClosestMatch(elt, function(e){return getAttributeValue(e,"hx-target") !== null});
|
||||
if (explicitTarget) {
|
||||
if (explicitTarget && getAttributeInheritance(elt, explicitTarget, "hx-target") !== "unset") {
|
||||
var targetStr = getAttributeValue(explicitTarget, "hx-target");
|
||||
if (targetStr === "this") {
|
||||
return explicitTarget;
|
||||
@ -2518,9 +2521,9 @@ return (function () {
|
||||
target = beforeSwapDetails.target; // allow re-targeting
|
||||
serverResponse = beforeSwapDetails.serverResponse; // allow updating content
|
||||
isError = beforeSwapDetails.isError; // allow updating error
|
||||
|
||||
|
||||
responseInfo.failed = isError; // Make failed property available to response events
|
||||
responseInfo.successful = !isError; // Make successful property available to response events
|
||||
responseInfo.successful = !isError; // Make successful property available to response events
|
||||
|
||||
if (beforeSwapDetails.shouldSwap) {
|
||||
if (xhr.status === 286) {
|
||||
|
@ -55,7 +55,7 @@ describe("hx-inherit attribute", function() {
|
||||
var btn = byId("bx1");
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
btn.innerHTML.should.equal('<span id="cta" class="">' + response + '</span>');
|
||||
btn.innerHTML.should.equal(response);
|
||||
});
|
||||
|
||||
it('same-element inheritance disable', function () {
|
||||
@ -69,21 +69,67 @@ describe("hx-inherit attribute", function() {
|
||||
btn.innerHTML.should.equal(response_inner);
|
||||
});
|
||||
|
||||
/* Does not work properly yet; how can btn.innerHTML be true, but at the same time div.parentElement.innerHTML not contain the btn itself anymore? */
|
||||
it('same-element inheritance disable with child nodes', function () {
|
||||
var response_inner = '<div id="snowflake" class="">Hello world</div>'
|
||||
var response = '<div id="unique">' + response_inner + '</div>'
|
||||
this.server.respondWith("GET", "/test", response);
|
||||
this.server.respondWith("GET", "/test2", 'unique-snowflake');
|
||||
|
||||
var div = make('<div hx-select="#snowflake" hx-target="#container" hx-get="/test" hx-swap="outerHTML" hx-inherit="false"><div id="container"><button id="bx1" hx-get="/test2" hx-trigger="click" hx-target="#target"><div id="target"></div></button></div></div>')
|
||||
var div = make('<div hx-select="#snowflake" hx-target="#container" hx-get="/test" hx-swap="outerHTML" hx-trigger="keyup" hx-inherit="false"><div id="container"><button id="bx1" hx-get="/test2" hx-trigger="click" hx-target="#target"><div id="target"></div></button></div></div>')
|
||||
var btn = byId("bx1");
|
||||
btn.click();
|
||||
this.server.respond();
|
||||
btn.innerHTML.should.equal('<div id="target" class="">unique-snowflake</div>');
|
||||
div.parentElement.innerHTML.should.equal('');
|
||||
var count = (div.innerHTML.match(/slowflake/g) || []).length;
|
||||
count.should.equal(1);
|
||||
var count = (div.parentElement.innerHTML.match(/snowflake/g) || []).length;
|
||||
count.should.equal(2); // hx-select of parent div and newly loaded inner content
|
||||
});
|
||||
|
||||
it('boosted element hx-inherit sanity check', function () {
|
||||
try {
|
||||
var request;
|
||||
var handler = htmx.on("htmx:beforeRequest", function (evt) {
|
||||
request = evt;
|
||||
});
|
||||
var div = make('<div hx-boost="true" hx-inherit="false"><a id="a1" href="/test">Click me</a></div>');
|
||||
var link = byId("a1");
|
||||
link.click();
|
||||
should.equal(request.detail.requestConfig.path, '/test');
|
||||
should.equal(request.detail.elt["htmx-internal-data"].boosted, true);
|
||||
} finally {
|
||||
htmx.off("htmx:beforeRequest", handler);
|
||||
}
|
||||
});
|
||||
|
||||
it('boosted element inheritance manual unset', function () {
|
||||
try {
|
||||
var request;
|
||||
var handler = htmx.on("htmx:beforeRequest", function (evt) {
|
||||
request = evt;
|
||||
});
|
||||
var div = make('<div hx-boost="true" hx-get="/test"><div hx-boost="unset"><a id="a1" href="/test">Click me</a></div></div>');
|
||||
var link = byId("a1");
|
||||
should.equal(link["htmx-internal-data"].boosted, undefined);
|
||||
} finally {
|
||||
htmx.off("htmx:beforeRequest", handler);
|
||||
}
|
||||
});
|
||||
|
||||
it('nested htmx-node with boosting parent', function () {
|
||||
try {
|
||||
var request;
|
||||
var handler = htmx.on("htmx:beforeRequest", function (evt) {
|
||||
request = evt;
|
||||
});
|
||||
var div = make('<div hx-boost="true" hx-target="#test" hx-inherit="false"><div id="test"></div><a id="a1" href="/test" hx-get="/test2">Click me</a></div>');
|
||||
var link = byId("a1");
|
||||
link.click();
|
||||
should.equal(request.detail.requestConfig.path, '/test2');
|
||||
should.equal(request.detail.elt["htmx-internal-data"].boosted, undefined);
|
||||
should.equal(request.detail.target.id, "a1");
|
||||
} finally {
|
||||
htmx.off("htmx:beforeRequest", handler);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
62
www/attributes/hx-inherit.md
Normal file
62
www/attributes/hx-inherit.md
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
layout: layout.njk
|
||||
title: </> htmx - hx-inherit
|
||||
---
|
||||
|
||||
## `hx-inherit`
|
||||
|
||||
The `hx-inherit` attribute allows you to control the automatic attribute inheritance of an element to its
|
||||
children nodes. One use case here is, to allow wrapping the whole page in `hx-boost`, while not having
|
||||
the configuration of that wrapper affect a more elaborated child node htmx configuration.
|
||||
|
||||
The default behavior for htmx is to inherit all attributes automatically.
|
||||
`hx-inherit` *disables* inheritance.
|
||||
|
||||
|
||||
htmx evaluates inheritance as follows:
|
||||
|
||||
* when `hx-boost` is not set
|
||||
* get all elements that defines a `hx-<verb>` (`hx-get`, `hx-post`, `hx-patch`, `hx-put` or `hx-delete`)
|
||||
* for each htmx attribute, either find the htmx attribute on the element the `hx-<verb>` was defined on or traverse up all the parent nodes
|
||||
* the first element's value the corresponding htmx attribute has been found on is then used
|
||||
* when `hx-boost` is set
|
||||
* make all `<a>` tags a `hx-get` with the link defined in the `href` attribute - [details](/attributes/hx-boost) which links hx-boost handles
|
||||
* then, same htmx attribute lookup procedure as above
|
||||
* when `hx-inherit` is set on a parent node
|
||||
* when a parent node has a matching htmx attribute during the automatic lookup outlined above
|
||||
* `hx-inherit="false"` disable all attribute inheritance
|
||||
* `hx-inherit="hx-select hx-get hx-target"` disable inheritance for only one or multiple specified attributes
|
||||
|
||||
|
||||
```html
|
||||
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-inherit="false">
|
||||
<a href="/page1">Go To Page 1</a> <!-- boosted with the attribute settings above -->
|
||||
<a href="/page2" hx-boost="unset">Go To Page 1</a> <!-- not boosted -->
|
||||
<button hx-get="/test" hx-target="this"></button> <!-- hx-select is not inherited -->
|
||||
</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-inherit="hx-target">
|
||||
<!-- hx-select is automatically set to parent's value; hx-target is not inherited -->
|
||||
<button hx-get="/test"></button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<div hx-target="#content">
|
||||
<div hx-boost="true" hx-select="#content" hx-inherit="hx-target">
|
||||
<!-- hx-select is automatically set to parent's value -->
|
||||
<!-- hx-target is inherited, because the direct parent does
|
||||
not specify hx-target, thus cannot disable inheritance -->
|
||||
<button hx-get="/test"></button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* `hx-inherit="false"` or `hx-inherit="hx-boost"` does not disable `hx-boost`; instead use `hx-boost="unset"` to disable inheritance
|
||||
* `hx-inherit="false"` or `hx-inherit="<attribute"` only affects the inheritance outcome, if the element also has the htmx attribute
|
||||
defined that is currently being looked up
|
||||
* Find out more about [Attribute Inheritance](/docs/#inheritance)
|
@ -511,6 +511,9 @@ be confirmed. We could add an `unset` directive on it like so:
|
||||
|
||||
The top two buttons would then show a confirm dialog, but the bottom cancel button would not.
|
||||
|
||||
Automatic inheritance can be further configured or disabled using [`hx-inherit`](/attributes/hx-inherit).
|
||||
|
||||
|
||||
## <a name="boosting"></a>[Boosting](#boosting)
|
||||
|
||||
Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](/attributes/hx-boost) attribute. This
|
||||
|
@ -31,6 +31,7 @@ title: </> htmx - Attributes
|
||||
| [`hx-history-elt`](/attributes/hx-history-elt) | the element to snapshot and restore during history navigation
|
||||
| [`hx-include`](/attributes/hx-include) | includes additional data in AJAX requests
|
||||
| [`hx-indicator`](/attributes/hx-indicator) | the element to put the `htmx-request` class on during the AJAX request
|
||||
| [`hx-inherit`](/attributes/hx-inherit) | control and disable automatic attribute inheritance for child nodes
|
||||
| [`hx-params`](/attributes/hx-params) | filters the parameters that will be submitted with a request
|
||||
| [`hx-patch`](/attributes/hx-patch) | issues a `PATCH` to the specified URL
|
||||
| [`hx-post`](/attributes/hx-post) | issues a `POST` to the specified URL
|
||||
|
Loading…
x
Reference in New Issue
Block a user