npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

pill

v1.4.6

Published

Dynamic content loading for static sites.

Downloads

241

Readme

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 KiB gzipped.
  • 🥤 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:

  1. Intercepts navigation attempts: links clicks and history navigation.
  2. Loads requested url using fetch.
  3. Grabs content from received HTML.
  4. 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

  1. Inject pill's <script> into page.
  2. Create content root element and give it id.
  3. Create loading indicator element.
  4. 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) -> void

Initialize 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) -> void

Handle page loading exception. By default is console.error.

PillOptions.onLoading()

(page:Page) -> void

Handle loading start.

Could be replaced with pill:loading Event listener.

PillOptions.onMounting()

(page:Page, url:URL, element:HTMLElement) -> void

Fires everytime new content is about to be loaded to the DOM.

Could be replaced with pill:mounting Event listener.

PillOptions.onReady()

(page:Page) -> void

Handle loading finish.

Could be replaced with pill:ready Event listener.

PillOptions.onUnmounting()

(page:Page, url:URL, element:HTMLElement) -> void

Fires 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) -> String

Get 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) -> Boolean

Determine wether previously loaded page should be loaded from server.

PillOptions.shouldServe()

(url:URL, target:HTMLElement) -> Boolean

Developer-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