-
Notifications
You must be signed in to change notification settings - Fork 160
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ticket #4641 - Use HTMX to load page content without genaral page st…
…ructure. Preload.
- Loading branch information
Showing
6 changed files
with
166 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// This adds the "preload" extension to htmx. By default, this will | ||
// preload the targets of any tags with `href` or `hx-get` attributes | ||
// if they also have a `preload` attribute as well. See documentation | ||
// for more details | ||
htmx.defineExtension("preload", { | ||
|
||
onEvent: function(name, event) { | ||
|
||
// Only take actions on "htmx:afterProcessNode" | ||
if (name !== "htmx:afterProcessNode") { | ||
return; | ||
} | ||
|
||
// SOME HELPER FUNCTIONS WE'LL NEED ALONG THE WAY | ||
|
||
// attr gets the closest non-empty value from the attribute. | ||
var attr = function(node, property) { | ||
if (node == undefined) {return undefined;} | ||
return node.getAttribute(property) || node.getAttribute("data-" + property) || attr(node.parentElement, property) | ||
} | ||
|
||
// load handles the actual HTTP fetch, and uses htmx.ajax in cases where we're | ||
// preloading an htmx resource (this sends the same HTTP headers as a regular htmx request) | ||
var load = function(node) { | ||
|
||
// Called after a successful AJAX request, to mark the | ||
// content as loaded (and prevent additional AJAX calls.) | ||
var done = function(html) { | ||
if (!node.preloadAlways) { | ||
node.preloadState = "DONE" | ||
} | ||
|
||
if (attr(node, "preload-images") == "true") { | ||
document.createElement("div").innerHTML = html // create and populate a node to load linked resources, too. | ||
} | ||
} | ||
|
||
return function() { | ||
|
||
// If this value has already been loaded, then do not try again. | ||
if (node.preloadState !== "READY") { | ||
return; | ||
} | ||
|
||
// Special handling for HX-GET - use built-in htmx.ajax function | ||
// so that headers match other htmx requests, then set | ||
// node.preloadState = TRUE so that requests are not duplicated | ||
// in the future | ||
var hxGet = node.getAttribute("hx-get") || node.getAttribute("data-hx-get") | ||
if (hxGet) { | ||
htmx.ajax("GET", hxGet, { | ||
source: node, | ||
handler:function(elt, info) { | ||
done(info.xhr.responseText); | ||
} | ||
}); | ||
return; | ||
} | ||
|
||
// Otherwise, perform a standard xhr request, then set | ||
// node.preloadState = TRUE so that requests are not duplicated | ||
// in the future. | ||
if (node.getAttribute("href")) { | ||
var r = new XMLHttpRequest(); | ||
r.open("GET", node.getAttribute("href")); | ||
r.onload = function() {done(r.responseText);}; | ||
r.send(); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
// This function processes a specific node and sets up event handlers. | ||
// We'll search for nodes and use it below. | ||
var init = function(node) { | ||
|
||
// If this node DOES NOT include a "GET" transaction, then there's nothing to do here. | ||
if (node.getAttribute("href") + node.getAttribute("hx-get") + node.getAttribute("data-hx-get") == "") { | ||
return; | ||
} | ||
|
||
// Guarantee that we only initialize each node once. | ||
if (node.preloadState !== undefined) { | ||
return; | ||
} | ||
|
||
// Get event name from config. | ||
var on = attr(node, "preload") || "mousedown" | ||
const always = on.indexOf("always") !== -1 | ||
if (always) { | ||
on = on.replace('always', '').trim() | ||
} | ||
|
||
// FALL THROUGH to here means we need to add an EventListener | ||
|
||
// Apply the listener to the node | ||
node.addEventListener(on, function(evt) { | ||
if (node.preloadState === "PAUSE") { // Only add one event listener | ||
node.preloadState = "READY"; // Required for the `load` function to trigger | ||
|
||
// Special handling for "mouseover" events. Wait 100ms before triggering load. | ||
if (on === "mouseover") { | ||
window.setTimeout(load(node), 100); | ||
} else { | ||
load(node)() // all other events trigger immediately. | ||
} | ||
} | ||
}) | ||
|
||
// Special handling for certain built-in event handlers | ||
switch (on) { | ||
|
||
case "mouseover": | ||
// Mirror `touchstart` events (fires immediately) | ||
node.addEventListener("touchstart", load(node)); | ||
|
||
// WHhen the mouse leaves, immediately disable the preload | ||
node.addEventListener("mouseout", function(evt) { | ||
if ((evt.target === node) && (node.preloadState === "READY")) { | ||
node.preloadState = "PAUSE"; | ||
} | ||
}) | ||
break; | ||
|
||
case "mousedown": | ||
// Mirror `touchstart` events (fires immediately) | ||
node.addEventListener("touchstart", load(node)); | ||
break; | ||
} | ||
|
||
// Mark the node as ready to run. | ||
node.preloadState = "PAUSE"; | ||
node.preloadAlways = always; | ||
htmx.trigger(node, "preload:init") // This event can be used to load content immediately. | ||
} | ||
|
||
// Search for all child nodes that have a "preload" attribute | ||
event.target.querySelectorAll("[preload]").forEach(function(node) { | ||
|
||
// Initialize the node with the "preload" attribute | ||
init(node) | ||
|
||
// Initialize all child elements that are anchors or have `hx-get` (use with care) | ||
node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init) | ||
}) | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters