pill v1.4.6
Pill adds dynamic content loading to static sites and makes content loading smooth for users. It's pretty small only 1.45 KiB minified and gzipped. It fits perfectly for static sites with WebComponents.
- 🐥 Tiny:
1.45 KiBgzipped. - 🥤 Easy-to-use: single function call.
- 🎮 Handful: hooks and callbacks could modify the default behavior.
Pill development started with the tweet by Andrey Sitnik @ai.
How pill works. It:
- Intercepts navigation attempts: links clicks and history navigation.
- Loads requested url using
fetch. - Grabs content from received HTML.
- Replaces current page content.
Initialize in one line:
pill('#content') // Yep, that's it.Table of Contents
Install
Include script from unpkg.com:
<script src="https://unpkg.com/pill@1/dist/pill.min.js"></script>⚠️ Remember about security! Add subresource integrity (SRI) checksum from checksum.txt.
Install via npm:
npm i pill
Usage
- Inject pill's
<script>into page. - Create content root element and give it id.
- Create loading indicator element.
- Initialize pill:
// Get loading indicator element const indicator = document.querySelector('#indicator') // Assign Pill to specified selector pill('#content', { onLoading() { // Show loading indicator indicator.style.display = 'initial' }, onReady() { // Hide loading indicator indicator.style.display = 'none' } })
Complete example
<html>
<head>
<title>Home</title>
<script src="https://unpkg.com/pill@1/dist/pill.min.js"></script>
<style>
/* global styles */
#indicator {
position: fixed;
top: 0;
right: 0;
display: none;
}
</style>
</head>
<body>
<div id="indicator">Loading...</div>
<div id="content">
<style>/* page styles */</style>
<!-- page content here -->
</div>
<script>
const indicator = document.querySelector('#indicator')
pill('#content', {
onLoading() {
// Show loading indicator
indicator.style.display = 'initial'
},
onReady() {
// Hide loading indicator
indicator.style.display = 'none'
}
})
</script>
</body>
</html>Each document of the site should surround #content element with the same HTML.
All page-related content should be located inside #content. It could be styles, scripts, etc.
Corner Cases
No script inside of the content element
Script elements placed inside of your content element wouldn't be evaluated after loading.
You should place all scripts out of your content element (in the head or body) and run JS manually.
This behavior prevents your site from memory licks and race conditions caused by inner scripts
different lifetime. And then you can react on page change with onReady hook to change your
app behavior.
Example:
<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="content"></div>
<!-- common scripts -->
<script src="/scripts/pill.js"></script>
<script src="/scripts/app.js"></script>
<script>
pill('#content', {
onMounting(page, url, element) {
// Init page, for example bind event listeners, start timers, etc.
App.initPage(url, element)
},
onUnmounting(page, url, element) {
// Uninitialise page, for example remove event listeners, stop timers, etc.
App.destroyPage(url, element)
},
})
App.initPage(new URL(window.location), document.querySelector('#content'))
</script>
</html>API
pill()
(selector:string, options:PillOptions) -> voidInitialize pill. Start listening for navigation attempts and history state changes. Puts loaded
content into selector element.
Events
You can handle Pill's events by binding handlers on document element:
document.addEventListener('pill:loading', (e) => {
e.detail.page; // Current page
})pill:error Event
{
detail: {
url: URL
element: HTMLElement
error: Error
}
}Is emitted when the new page loading has been started. This event wouldn't be emitted if page is cached.
Could be replaced with PillOptions.onLoading() hook.
pill:loading Event
{
detail: {
url: URL
element: HTMLElement
}
}Is emitted when the new page loading has been started. This event wouldn't be emitted if page is cached.
Could be replaced with PillOptions.onLoading() hook.
pill:mounting Event
{
detail: {
page: Page
url: URL
element: HTMLElement
}
}Is emitted when new page content is about to be added into the DOM.
Could be replaced with PillOptions.onMounting() hook.
pill:ready Event
{
detail: {
page: Page
url: URL
element: HTMLElement
}
}Is emitted when the requested page is mounted into DOM and no futher work would be done.
Could be replaced with PillOptions.onReady() hook.
pill:unmounting Event
{
detail: {
page: Page
url: URL
element: HTMLElement
}
}Is emitted when new page content is about to be removed from the DOM.
Could be replaced with PillOptions.onMounting() hook.
Hooks
PillOptions.onError()
(error) -> voidHandle page loading exception. By default is console.error.
PillOptions.onLoading()
(page:Page) -> voidHandle loading start.
Could be replaced with pill:loading Event listener.
PillOptions.onMounting()
(page:Page, url:URL, element:HTMLElement) -> voidFires everytime new content is about to be loaded to the DOM.
Could be replaced with pill:mounting Event listener.
PillOptions.onReady()
(page:Page) -> voidHandle loading finish.
Could be replaced with pill:ready Event listener.
PillOptions.onUnmounting()
(page:Page, url:URL, element:HTMLElement) -> voidFires everytime content is about to be removed from the DOM.
Could be replaced with pill:unmounting Event listener.
Other options
PillOptions.fromError()
(error:Error) -> {title, content}Use it to display notification when something went wrong.
If an error was thrown while handling request. You still able
to render content using method fromError
PillOptions.getKeyFromUrl()
(url:URL) -> StringGet cache key from URL. It's useful when URL contains query params which are unknown to server and could not affect response. By default any new pathname and search string combination will cause new request.
PillOptions.shouldReload()
(page:Page) -> BooleanDetermine wether previously loaded page should be loaded from server.
PillOptions.shouldServe()
(url:URL, target:HTMLElement) -> BooleanDeveloper-defined logic to determine whether the URL could be served by Pill.
If you return false then the link will be served by browser.
License
MIT © Rumkin
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
12 years ago
12 years ago
12 years ago