mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-03 07:45:21 +00:00
Merge branch 'dev' into hx-on-experiment
# Conflicts: # src/htmx.js # test/scratch/scratch.html
This commit is contained in:
commit
2713a3ada0
61
src/htmx.js
61
src/htmx.js
@ -70,6 +70,7 @@ return (function () {
|
|||||||
scrollBehavior: 'smooth',
|
scrollBehavior: 'smooth',
|
||||||
defaultFocusScroll: false,
|
defaultFocusScroll: false,
|
||||||
getCacheBusterParam: false,
|
getCacheBusterParam: false,
|
||||||
|
globalViewTransitions: false,
|
||||||
},
|
},
|
||||||
parseInterval:parseInterval,
|
parseInterval:parseInterval,
|
||||||
_:internalEval,
|
_:internalEval,
|
||||||
@ -471,7 +472,10 @@ return (function () {
|
|||||||
function removeElement(elt, delay) {
|
function removeElement(elt, delay) {
|
||||||
elt = resolveTarget(elt);
|
elt = resolveTarget(elt);
|
||||||
if (delay) {
|
if (delay) {
|
||||||
setTimeout(function(){removeElement(elt);}, delay)
|
setTimeout(function(){
|
||||||
|
removeElement(elt);
|
||||||
|
elt = null;
|
||||||
|
}, delay);
|
||||||
} else {
|
} else {
|
||||||
elt.parentElement.removeChild(elt);
|
elt.parentElement.removeChild(elt);
|
||||||
}
|
}
|
||||||
@ -480,7 +484,10 @@ return (function () {
|
|||||||
function addClassToElement(elt, clazz, delay) {
|
function addClassToElement(elt, clazz, delay) {
|
||||||
elt = resolveTarget(elt);
|
elt = resolveTarget(elt);
|
||||||
if (delay) {
|
if (delay) {
|
||||||
setTimeout(function(){addClassToElement(elt, clazz);}, delay)
|
setTimeout(function(){
|
||||||
|
addClassToElement(elt, clazz);
|
||||||
|
elt = null;
|
||||||
|
}, delay);
|
||||||
} else {
|
} else {
|
||||||
elt.classList && elt.classList.add(clazz);
|
elt.classList && elt.classList.add(clazz);
|
||||||
}
|
}
|
||||||
@ -489,7 +496,10 @@ return (function () {
|
|||||||
function removeClassFromElement(elt, clazz, delay) {
|
function removeClassFromElement(elt, clazz, delay) {
|
||||||
elt = resolveTarget(elt);
|
elt = resolveTarget(elt);
|
||||||
if (delay) {
|
if (delay) {
|
||||||
setTimeout(function(){removeClassFromElement(elt, clazz);}, delay)
|
setTimeout(function(){
|
||||||
|
removeClassFromElement(elt, clazz);
|
||||||
|
elt = null;
|
||||||
|
}, delay);
|
||||||
} else {
|
} else {
|
||||||
if (elt.classList) {
|
if (elt.classList) {
|
||||||
elt.classList.remove(clazz);
|
elt.classList.remove(clazz);
|
||||||
@ -1401,6 +1411,7 @@ return (function () {
|
|||||||
} else if (triggerSpec.delay) {
|
} else if (triggerSpec.delay) {
|
||||||
elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay);
|
elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay);
|
||||||
} else {
|
} else {
|
||||||
|
triggerEvent(elt, 'htmx:trigger')
|
||||||
handler(elt, evt);
|
handler(elt, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1693,6 +1704,13 @@ return (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!explicitAction && hasAttribute(elt, 'hx-trigger')) {
|
||||||
|
explicitAction = true
|
||||||
|
triggerSpecs.forEach(function(triggerSpec) {
|
||||||
|
// For "naked" triggers, don't do anything at all
|
||||||
|
addTriggerHandler(elt, triggerSpec, nodeData, function () { })
|
||||||
|
})
|
||||||
|
}
|
||||||
return explicitAction;
|
return explicitAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1777,7 +1795,7 @@ return (function () {
|
|||||||
if (elt.querySelectorAll) {
|
if (elt.querySelectorAll) {
|
||||||
var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : "";
|
var boostedElts = hasChanceOfBeingBoosted() ? ", a, form" : "";
|
||||||
var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," +
|
var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", [hx-sse], [data-hx-sse], [hx-ws]," +
|
||||||
" [data-hx-ws], [hx-ext], [data-hx-ext], [hx-on], [data-hx-on]");
|
" [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]");
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -2454,6 +2472,9 @@ return (function () {
|
|||||||
if (modifier.indexOf("settle:") === 0) {
|
if (modifier.indexOf("settle:") === 0) {
|
||||||
swapSpec["settleDelay"] = parseInterval(modifier.substr(7));
|
swapSpec["settleDelay"] = parseInterval(modifier.substr(7));
|
||||||
}
|
}
|
||||||
|
if (modifier.indexOf("transition:") === 0) {
|
||||||
|
swapSpec["transition"] = modifier.substr(11) === "true";
|
||||||
|
}
|
||||||
if (modifier.indexOf("scroll:") === 0) {
|
if (modifier.indexOf("scroll:") === 0) {
|
||||||
var scrollSpec = modifier.substr(7);
|
var scrollSpec = modifier.substr(7);
|
||||||
var splitSpec = scrollSpec.split(":");
|
var splitSpec = scrollSpec.split(":");
|
||||||
@ -3185,9 +3206,13 @@ return (function () {
|
|||||||
var swapSpec = getSwapSpecification(elt, swapOverride);
|
var swapSpec = getSwapSpecification(elt, swapOverride);
|
||||||
|
|
||||||
target.classList.add(htmx.config.swappingClass);
|
target.classList.add(htmx.config.swappingClass);
|
||||||
|
|
||||||
|
// optional transition API promise callbacks
|
||||||
|
var settleResolve = null;
|
||||||
|
var settleReject = null;
|
||||||
|
|
||||||
var doSwap = function () {
|
var doSwap = function () {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
var activeElt = document.activeElement;
|
var activeElt = document.activeElement;
|
||||||
var selectionInfo = {};
|
var selectionInfo = {};
|
||||||
try {
|
try {
|
||||||
@ -3286,6 +3311,7 @@ return (function () {
|
|||||||
}
|
}
|
||||||
handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt);
|
handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt);
|
||||||
}
|
}
|
||||||
|
maybeCall(settleResolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swapSpec.settleDelay > 0) {
|
if (swapSpec.settleDelay > 0) {
|
||||||
@ -3295,10 +3321,34 @@ return (function () {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
triggerErrorEvent(elt, 'htmx:swapError', responseInfo);
|
triggerErrorEvent(elt, 'htmx:swapError', responseInfo);
|
||||||
|
maybeCall(settleReject);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var shouldTransition = htmx.config.globalViewTransitions
|
||||||
|
if(swapSpec.hasOwnProperty('transition')){
|
||||||
|
shouldTransition = swapSpec.transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shouldTransition &&
|
||||||
|
triggerEvent(elt, 'htmx:beforeTransition', responseInfo) &&
|
||||||
|
typeof Promise !== "undefined" && document.startViewTransition){
|
||||||
|
var settlePromise = new Promise(function (_resolve, _reject) {
|
||||||
|
settleResolve = _resolve;
|
||||||
|
settleReject = _reject;
|
||||||
|
});
|
||||||
|
// wrap the original doSwap() in a call to startViewTransition()
|
||||||
|
var innerDoSwap = doSwap;
|
||||||
|
doSwap = function() {
|
||||||
|
document.startViewTransition(function () {
|
||||||
|
innerDoSwap();
|
||||||
|
return settlePromise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (swapSpec.swapDelay > 0) {
|
if (swapSpec.swapDelay > 0) {
|
||||||
setTimeout(doSwap, swapSpec.swapDelay)
|
setTimeout(doSwap, swapSpec.swapDelay)
|
||||||
} else {
|
} else {
|
||||||
@ -3460,6 +3510,7 @@ return (function () {
|
|||||||
};
|
};
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event
|
triggerEvent(body, 'htmx:load', {}); // give ready handlers a chance to load up before firing this event
|
||||||
|
body = null; // kill reference for gc
|
||||||
}, 0);
|
}, 0);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ describe("hx-trigger attribute", function(){
|
|||||||
it('non-default value works', function()
|
it('non-default value works', function()
|
||||||
{
|
{
|
||||||
this.server.respondWith("GET", "/test", "Clicked!");
|
this.server.respondWith("GET", "/test", "Clicked!");
|
||||||
|
|
||||||
var form = make('<form hx-get="/test" hx-trigger="click">Click Me!</form>');
|
var form = make('<form hx-get="/test" hx-trigger="click">Click Me!</form>');
|
||||||
form.click();
|
form.click();
|
||||||
form.innerHTML.should.equal("Click Me!");
|
form.innerHTML.should.equal("Click Me!");
|
||||||
@ -756,5 +755,33 @@ describe("hx-trigger attribute", function(){
|
|||||||
div.innerHTML.should.equal("test 1");
|
div.innerHTML.should.equal("test 1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("fires the htmx:trigger event when an AJAX attribute is specified", function () {
|
||||||
|
var param = "foo"
|
||||||
|
var handler = htmx.on("htmx:trigger", function (evt) {
|
||||||
|
param = "bar"
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
this.server.respondWith("GET", "/test1", "test 1");
|
||||||
|
var div = make('<button hx-get="/test1">Submit</button>');
|
||||||
|
div.click();
|
||||||
|
should.equal(param, "bar");
|
||||||
|
} finally {
|
||||||
|
htmx.off("htmx:trigger", handler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fires the htmx:trigger event when no AJAX attribute is specified", function () {
|
||||||
|
var param = "foo"
|
||||||
|
var handler = htmx.on("htmx:trigger", function (evt) {
|
||||||
|
param = "bar"
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
var div = make('<button hx-trigger="click">Submit</button>');
|
||||||
|
div.click();
|
||||||
|
should.equal(param, "bar");
|
||||||
|
} finally {
|
||||||
|
htmx.off("htmx:trigger", handler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -14,6 +14,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-from-right {
|
||||||
|
from { transform: translateX(30px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-to-left {
|
||||||
|
to { transform: translateX(-30px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root) {
|
||||||
|
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
|
||||||
|
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
|
||||||
|
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@ -48,6 +78,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2>Server Options</h2>
|
<h2>Server Options</h2>
|
||||||
<button onclick="server.respond()">Server Respond</button>
|
<button onclick="server.respond()">Server Respond</button>
|
||||||
<br/>
|
<br/>
|
||||||
@ -62,16 +93,17 @@ Autorespond: <input id="autorespond" type="checkbox" onclick="toggleAutoRespond(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
this.server.respondWith("GET", "/demo", 'demo response...');
|
let requestCount = 0;
|
||||||
|
this.server.respondWith("GET", "/demo", function(xhr){
|
||||||
|
let randomStr = (Math.random() + 1).toString(36).substring(7);
|
||||||
|
xhr.respond(200, {}, "Request #" + requestCount++ + " : " + randomStr)
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button>First</button>
|
<button hx-get="/demo"
|
||||||
<input hx-get="/demo" hx-target="next output" hx-trigger="keyup changed" hx-on="htmx:beforeRequest: console.log('before', event);
|
hx-target="next h1"
|
||||||
console.log({foo:10,
|
hx-swap="innerHTML transition:true">Issue Request</button>
|
||||||
bar: 20});
|
<h1 id="h1">--</h1>
|
||||||
htmx:afterRequest: console.log('after', event);
|
|
||||||
"/>
|
|
||||||
<output>--</output>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -35,6 +35,12 @@ The `div` will issue a request to `/example` and append the returned content aft
|
|||||||
|
|
||||||
The `hx-swap` attributes supports modifiers for changing the behavior of the swap. They are outlined below.
|
The `hx-swap` attributes supports modifiers for changing the behavior of the swap. They are outlined below.
|
||||||
|
|
||||||
|
#### Transition: `transition`
|
||||||
|
|
||||||
|
If you want to use the new (View Transitions)[https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API] API
|
||||||
|
when a swap occurs, you can use the `transition:true` option for your swap. You can also enable this feature globally by
|
||||||
|
setting the `htmx.config.globalViewTransitions` config setting to `true`.
|
||||||
|
|
||||||
#### Timing: `swap` & `settle`
|
#### Timing: `swap` & `settle`
|
||||||
|
|
||||||
You can modify the amount of time that htmx will wait after receiving a response to swap the content
|
You can modify the amount of time that htmx will wait after receiving a response to swap the content
|
||||||
|
@ -148,3 +148,4 @@ The AJAX request can be triggered via Javascript [`htmx.trigger()`](/api#trigger
|
|||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
* `hx-trigger` is not inherited
|
* `hx-trigger` is not inherited
|
||||||
|
* `hx-trigger` can be used without an AJAX request, in which case it will only fire the `htmx:trigger` event
|
||||||
|
17
www/docs.md
17
www/docs.md
@ -417,6 +417,22 @@ The following extensions are available for morph-style swaps:
|
|||||||
* [Idiomorph](https://github.com/bigskysoftware/idiomorph#htmx) - A newer morphing algorithm developed by us, the creators
|
* [Idiomorph](https://github.com/bigskysoftware/idiomorph#htmx) - A newer morphing algorithm developed by us, the creators
|
||||||
of htmx. Idiomorph will be available out of the box in htmx 2.0.
|
of htmx. Idiomorph will be available out of the box in htmx 2.0.
|
||||||
|
|
||||||
|
#### <a name="view-transitions"></a> [View Transitions](#view-transitions)
|
||||||
|
|
||||||
|
The new, experimental [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
|
||||||
|
gives developers a way to create an animated transition between different DOM states. It is still in active development
|
||||||
|
and is not available in all browsers, but htmx provides a way to work with this new API that falls back to the non-transition
|
||||||
|
mechanism if the API is not available in a given browser.
|
||||||
|
|
||||||
|
You can experiment with this new API using the following approaches:
|
||||||
|
|
||||||
|
* Set the `htmx.config.globalViewTransitions` config variable to `true` to use transitions for all swaps
|
||||||
|
* Use the `transition:true` option in the `hx-swap` attribute
|
||||||
|
* If an element swap is going to be transitioned due to either of the above configurations, you may catch the
|
||||||
|
`htmx:beforeTransition` event and call `preventDefault()` on it to cancel the transition.
|
||||||
|
|
||||||
|
View Transitions can be configured using CSS, as outlined in [the Chrome documentation for the feature](https://developer.chrome.com/docs/web-platform/view-transitions/#simple-customization).
|
||||||
|
|
||||||
### <a name="synchronization"></a> [Synchronization](#synchronization)
|
### <a name="synchronization"></a> [Synchronization](#synchronization)
|
||||||
|
|
||||||
Often you want to coordinate the requests between two elements. For example, you may want a request from one element
|
Often you want to coordinate the requests between two elements. For example, you may want a request from one element
|
||||||
@ -1402,6 +1418,7 @@ listed below:
|
|||||||
| `htmx.config.timeout` | defaults to 0 in milliseconds |
|
| `htmx.config.timeout` | defaults to 0 in milliseconds |
|
||||||
| `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](/attributes/hx-swap/#focus-scroll) swap modifier. |
|
| `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](/attributes/hx-swap/#focus-scroll) swap modifier. |
|
||||||
| `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will include a cache-busting parameter in `GET` requests to avoid caching partial responses by the browser |
|
| `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will include a cache-busting parameter in `GET` requests to avoid caching partial responses by the browser |
|
||||||
|
| `htmx.config.globalViewTransitions` | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content. |
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
90
www/essays/architectural-sympathy.md
Normal file
90
www/essays/architectural-sympathy.md
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
layout: layout.njk
|
||||||
|
title: Architectural Sympathy
|
||||||
|
---
|
||||||
|
|
||||||
|
# Mechanical Sympathy & Architectural Sympathy
|
||||||
|
|
||||||
|
> You don’t have to be an engineer to be be a racing driver, but you do have to have Mechanical Sympathy.
|
||||||
|
|
||||||
|
_-Jackie Stewart, racing driver_
|
||||||
|
|
||||||
|
The term "mechanical sympathy" was originally coined by Jackie Steward to capture a characteristic
|
||||||
|
of race car drivers, who needed a deep and intuitive understanding of how a race car worked in order
|
||||||
|
to get the best possible performance out of the vehicle.
|
||||||
|
|
||||||
|
This term was applied to software development by Martin Thompson when discussing his [LMAX](https://martinfowler.com/articles/lmax.html)
|
||||||
|
architecture, which utilized a low level and intuitive understanding of how his cloud system functioned
|
||||||
|
in order to maximize the performance of it. Thompson maintained [a blog](https://mechanical-sympathy.blogspot.com/)
|
||||||
|
on the topic for many years, and it is well worth going back and reading the posts there.
|
||||||
|
|
||||||
|
## Architectural Sympathy
|
||||||
|
|
||||||
|
In this brief essay I want to propose another concept and design principle, that of _Architectural Sympathy_:
|
||||||
|
|
||||||
|
> Architectural Sympathy is the characteristic of one piece of software adopting and conforming to the architectural
|
||||||
|
> design of another piece of software
|
||||||
|
|
||||||
|
This is a design principle that I have kept in mind when designing [htmx](https://htmx.org) and
|
||||||
|
[hyperscript](https://hyperscript.org) and I wanted to write it down for reference and so others can think about,
|
||||||
|
criticize and improve it.
|
||||||
|
|
||||||
|
### htmx's Architectural Sympathy for The Web
|
||||||
|
|
||||||
|
htmx is architecturally sympathetic to The Web because it adopts the underlying [REST-ful](/essays/hateoas) architecture
|
||||||
|
of The Web: exchanging _hypermedia_ in a REST-ful manner with a hypermedia server. As much as is practical, htmx takes
|
||||||
|
design cues from the existing Web infrastructure:
|
||||||
|
|
||||||
|
* It mimics the core hypermedia-exchange mechanic of links and forms
|
||||||
|
* It uses CSS selectors for targeting
|
||||||
|
* It uses standard URL paths for designating end points
|
||||||
|
* It uses the standard API language for specifying swap types
|
||||||
|
* Etc.
|
||||||
|
|
||||||
|
htmx attempts to _fold in_ to the existing conceptual architecture of The Web, rather than replace it.
|
||||||
|
|
||||||
|
This is in contrast with the [SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA) approach to building web
|
||||||
|
applications. Most SPA frameworks have little architectural sympathy with the original web model. Rather, they largely
|
||||||
|
_replace_ the original, REST-ful, hypermedia-oriented architecture of the web in favor of a more thick-client like
|
||||||
|
architecture, exchanging information over an
|
||||||
|
[RPC-like fixed-data format](/essays/how-did-rest-come-to-mean-the-opposite-of-rest/) network architecture.
|
||||||
|
|
||||||
|
### Advantages Of The Architecturally Sympathetic Approach
|
||||||
|
|
||||||
|
If a new piece of software maintains architectural sympathy with an original piece of software, the following advantages
|
||||||
|
are obtained:
|
||||||
|
|
||||||
|
* A developer who is familiar with the original piece of software does not need to learn a whole new conceptual approach
|
||||||
|
when using the new piece of software.
|
||||||
|
* The design constraints of the original piece of software offer a framework within which to evaluate features for the
|
||||||
|
new piece of software. This makes it easier to [say "no"](https://grugbrain.dev/#grug-on-saying-no) as you develop the
|
||||||
|
new software. ("The enemy of art is the absence of limitations." --[Orson Welles](https://quoteinvestigator.com/2014/05/24/art-limit/))
|
||||||
|
* Experience gained from working with the original piece of software can directly inform the design and implementation of
|
||||||
|
the new software
|
||||||
|
* There will likely be a subjective feeling of "fit" between the new and original software for users of the new software
|
||||||
|
|
||||||
|
### Disadvantages Of The Architecturally Sympathetic Approach
|
||||||
|
|
||||||
|
Of course, as with any design principle, there are trade-offs when using Architectural Sympathy:
|
||||||
|
|
||||||
|
* The shortcomings of the original piece of software are likely to be found in some way in the new software
|
||||||
|
* The design constraints impressed on the new software by the older software may be so oppressive as to limit progress
|
||||||
|
and functionality in the new software
|
||||||
|
* It may be difficult for developers to "see the point" of the new software, if it feels too close to the original software
|
||||||
|
* By maintaining architectural sympathy with the older, original software, the new software risks appearing old itself,
|
||||||
|
a danger in the software business that has often favored new and exciting approaches to problems.
|
||||||
|
* You may not be able to layer as many new concepts as some users might like on top of the original software
|
||||||
|
|
||||||
|
## Craftsmanship & Architectural Sympathy
|
||||||
|
|
||||||
|
A non-software example of architectural sympathy that I like to point to are medieval cathedrals: these cathedrals were
|
||||||
|
often built, rebuilt and improved over centuries by many different builders and architects (such as they were). And yet
|
||||||
|
they were able, over those centuries, to maintain a high level of architectural sympathy with the earlier workers.
|
||||||
|
|
||||||
|
Rather than focusing on radically new approaches to building, workers focused on maintaining a coherent whole and, within
|
||||||
|
that framework, on the craftsmanship of their individual contributions. Yes, there were flourishes and changes along the
|
||||||
|
way, but these typically did not sacrifice the conceptual coherence of the whole for the sake of innovation.
|
||||||
|
|
||||||
|
Adopting an architecturally sympathetic mindset in software development often means sacrificing how you would like to
|
||||||
|
do things in favor of how an original piece of software did things. While this constraint can chafe at times, it can
|
||||||
|
also produce well crafted software that is harmonious and that dovetails well with existing software.
|
@ -134,6 +134,20 @@ the documentation on [configuring swapping](/docs#modifying_swapping_behavior_wi
|
|||||||
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
|
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
|
||||||
* `detail.target` - the target of the swap
|
* `detail.target` - the target of the swap
|
||||||
|
|
||||||
|
### <a name="htmx:beforeTransition"></a> Event - [`htmx:beforeTransition`](#htmx:beforeTransition)
|
||||||
|
|
||||||
|
This event is triggered before a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
|
||||||
|
wrapped swap occurs. If the event is cancelled, the View Transition will not occur and the normal swapping logic will
|
||||||
|
happen instead.
|
||||||
|
|
||||||
|
##### Details
|
||||||
|
|
||||||
|
* `detail.elt` - the element that dispatched the request
|
||||||
|
* `detail.xhr` - the `XMLHttpRequest`
|
||||||
|
* `detail.requestConfig` - the configuration of the AJAX request
|
||||||
|
* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes)
|
||||||
|
* `detail.target` - the target of the swap
|
||||||
|
|
||||||
### <a name="htmx:configRequest"></a> Event - [`htmx:configRequest`](#htmx:configRequest)
|
### <a name="htmx:configRequest"></a> Event - [`htmx:configRequest`](#htmx:configRequest)
|
||||||
|
|
||||||
This event is triggered after htmx has collected parameters for inclusion in the request. It can be
|
This event is triggered after htmx has collected parameters for inclusion in the request. It can be
|
||||||
@ -399,6 +413,16 @@ Timeout time can be set using `htmx.config.timeout` or per element using [`hx-re
|
|||||||
* `detail.target` - the target of the request
|
* `detail.target` - the target of the request
|
||||||
* `detail.requestConfig` - the configuration of the AJAX request
|
* `detail.requestConfig` - the configuration of the AJAX request
|
||||||
|
|
||||||
|
### <a name="htmx:trigger"></a> Event - [`htmx:trigger`](#htmx:trigger)
|
||||||
|
|
||||||
|
This event is triggered whenever an AJAX request would be, even if no AJAX request is specified. It
|
||||||
|
is primarily intended to allow `hx-trigger` to execute client-side scripts; AJAX requests have more
|
||||||
|
granular events available, like [`htmx:beforeRequest`](#htmx:beforeRequest) or [`htmx:afterSend`](#htmx:afterSend).
|
||||||
|
|
||||||
|
##### Details
|
||||||
|
|
||||||
|
* `detail.elt` - the element that triggered the request
|
||||||
|
|
||||||
### <a name="htmx:validation:validate"></a> Event - [htmx:validation:validate](#htmx:validation:validate)
|
### <a name="htmx:validation:validate"></a> Event - [htmx:validation:validate](#htmx:validation:validate)
|
||||||
|
|
||||||
This event is triggered before an element is validated. It can be used with the `elt.setCustomValidity()` method
|
This event is triggered before an element is validated. It can be used with the `elt.setCustomValidity()` method
|
||||||
|
@ -163,6 +163,7 @@ The table below lists all other attributes available in htmx.
|
|||||||
| [`htmx:swapError`](/events#htmx:swapError) | triggered when an error occurs during the swap phase
|
| [`htmx:swapError`](/events#htmx:swapError) | triggered when an error occurs during the swap phase
|
||||||
| [`htmx:targetError`](/events#htmx:targetError) | triggered when an invalid target is specified
|
| [`htmx:targetError`](/events#htmx:targetError) | triggered when an invalid target is specified
|
||||||
| [`htmx:timeout`](/events#htmx:timeout) | triggered when a request timeout occurs
|
| [`htmx:timeout`](/events#htmx:timeout) | triggered when a request timeout occurs
|
||||||
|
| [`htmx:trigger`](/events#htmx:trigger) | triggered by the event specified in `hx-trigger`
|
||||||
| [`htmx:validation:validate`](/events#htmx:validation:validate) | triggered before an element is validated
|
| [`htmx:validation:validate`](/events#htmx:validation:validate) | triggered before an element is validated
|
||||||
| [`htmx:validation:failed`](/events#htmx:validation:failed) | triggered when an element fails validation
|
| [`htmx:validation:failed`](/events#htmx:validation:failed) | triggered when an element fails validation
|
||||||
| [`htmx:validation:halted`](/events#htmx:validation:halted) | triggered when a request is halted due to validation errors
|
| [`htmx:validation:halted`](/events#htmx:validation:halted) | triggered when a request is halted due to validation errors
|
||||||
|
Loading…
x
Reference in New Issue
Block a user