Dynamic Page Loader
An abstract / multi-purpose component that loads parts of a page dynamically. Intends to solve:
- paginations
- infinite scroll (TBD, needs
append
mode) - facets
Loads content of a web page dynamically when the user navigates to a different URL. It thereby
enables seamless page transitions between two different static web pages, similar to the ones
provided by Next.js or Gatsby.
Supports:
- Elements that are preserved in the DOM when the page changes (
data-preserve-id
) - Hook to test if the given URL should be loaded dynamically
- Hook to execute a script before the page content is changed (e.g. for animations)
- Hook to execute a script after the page content was changed (e.g. for animations)
- Only adds dynamic click handler to elements once (to not fire url change multiple times for
elements that are preserved over a page change)
Attention!
- For now only modifies HTMLElements – and no other node types (text, comments, etc.)
Important
There are certain limitations concerning the use of JavaScript, as the global scope is preserved
over page loads:
- Don't use any global variables or functions, especially not
const
s (would throw an already
declared error). Use IFFEs e.g. where
appropriate. - Don't use
type="module"
script tags for code that should be executed on page load. Module
script tags will only execute when the page is initially loaded.
Be aware that CSS is handled differently:
- The sort order of
<link>
elements will not be adjusted to the element order of the new page,
as moving <link>
elements around causes flickering.
Example
import {
applyChanges,
handleLinkClicks,
handlePopState,
loadFile,
createNode,
canBeIdentical,
isIdentical,
applyAttributes,
} from '@joinbox/ui-components/DynamicPageLoader';
handleLinkClicks({
linkElements: document.querySelectorAll('a'),
checkLink: link => link.startsWith('/'),
});
handlePopState();
window.addEventListener(
'urlchange',
async(ev) => {
const { url } = ev.detail;
const dom = await loadFile(url);
const updateNode = node => (node.tagName === 'SCRIPT' ? createNode(document, node) : node);
applyChanges({
originalNode: document.querySelector('body'),
newNode: dom.querySelector('body'),
canBeIdentical,
isIdentical,
updateNode,
updateAttributes: applyAttributes,
});
applyChanges({
originalNode: document.querySelector('head'),
newNode: dom.querySelector('head'),
canBeIdentical,
isIdentical,
updateNode,
updateAttributes: applyAttributes,
});
setTimeout(() => {
const method = url.includes('about') ? 'add' : 'remove';
document.querySelector('.header').classList[method]('about');
});
},
{ once: true },
);
Functions
handleLinkClicks
Adds click event listener to linkElements
passed in; executes pushState
on click and dispatches
urlchange
event on window
.
Parameters
linkElements
: iterable collection of DOM elements (i.e. links) whose behaviour should be
'hijacked' and not trigger a page refresh in the browser.checkLink
: function that takes a single parameter url
that corresponds to the link's `href``
attribute. Return falsy value if the link should be handled by the browser and not by
DynamicPageLoader.
handlePopState
Dispatches a urlchange
event on window when a popState
event occurs on window
.
Parameters
None
loadFile
Loads a remote file (through fetch
) and returns its content as a DOM tree.
Parameters
createNode
Takes a HTMLElement and creates a new element (through document.createElement
) with the same
tag name and attributes. Needed to e.g. create a proper script
tag.
Parameters
document
: reference to the document that the element should be created fornode
: HTMLElement that should be cloned/created
applyChanges
Takes two DOM nodes and applies the changes from newNode
to originalNode
.
Parameters
originalNode
: HTMLElement that will be patched with the changes in newNodenewNode
: HTMLElement whose contents will be added to/removed from originalNode
canBeIdentical
: function that thakes a single parameter element (HTMLElement
); return true if
the element might be preserved (isIdentical
might return true). Only needed to improve speed.isIdentical
: function that takes two parameters (both HTMLElement
s) and returns true if both
elements are considered identical. The original element will be preserved in the DOM, the new
element will be ignored.updateNode
(optional): function that takes a single parameter (HTMLElement
) and is expected
to return a modified HTMLElement, if you wish to change the HTMLElement before it is added to the
DOM. Needed to e.g. create a <script>
element from scratch to make sure it is executed.updateAttributes
(optional): function that takes two parameters (newElement, originalelement
,
both HTMLElement
s) and copies attributes from preserved newElement
to originalElement
. By
default, applyChanges
does not update attributes on preserved elements.
canBeIdentical
A default function for the canBeIdentical
argument of applyChanges
. Returns true if
- both elements are
link
or meta
elements - element has an attribute
data-preserve-id
.
isIdentical
A default function for the isIdentical
argument of applyChanges
. Returns true if
- both elements are
link
or meta
elements and have exactly the same attributes - both elements have a
data-preserve-id
attribute that is identical.
applyAttributes
A default function for the updateAttributes
argument of applyChanges
. Copies all changed or
new attributes from origin
to target
and removes the ones from target
that are not present
on origin
.