{"id":13394072,"url":"https://github.com/rumkin/pill","last_synced_at":"2025-04-05T05:10:09.344Z","repository":{"id":55931097,"uuid":"178313264","full_name":"rumkin/pill","owner":"rumkin","description":"Add dynamic content loading to static sites with only 1 KiB of JS","archived":false,"fork":false,"pushed_at":"2020-12-12T21:37:26.000Z","size":202,"stargazers_count":383,"open_issues_count":12,"forks_count":19,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-05-13T11:04:27.651Z","etag":null,"topics":["dynamic-loading","static-sites"],"latest_commit_sha":null,"homepage":"https://rumkin.github.io/pill/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rumkin.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-29T01:57:12.000Z","updated_at":"2024-04-03T19:48:42.000Z","dependencies_parsed_at":"2022-08-15T09:40:53.623Z","dependency_job_id":null,"html_url":"https://github.com/rumkin/pill","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumkin%2Fpill","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumkin%2Fpill/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumkin%2Fpill/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumkin%2Fpill/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rumkin","download_url":"https://codeload.github.com/rumkin/pill/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289429,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dynamic-loading","static-sites"],"created_at":"2024-07-30T17:01:08.033Z","updated_at":"2025-04-05T05:10:09.329Z","avatar_url":"https://github.com/rumkin.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg width=\"220\" alt=\"Pill logo\" src=\"docs/cover.png\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.com/package/pill\"\u003e\n    \u003cimg alt=\"badge: npm version\" src=\"https://img.shields.io/npm/v/pill.svg?style=flat-square\" /\u003e\n  \u003c/a\u003e\n  \u003cimg alt=\"badge: size 1.45 KiB\" src=\"https://img.shields.io/badge/size-1.45%20KiB-blue.svg?style=flat-square\" /\u003e\n  \u003cimg alt=\"badge: deps 0\" src=\"https://img.shields.io/badge/deps-0-blue.svg?style=flat-square\" /\u003e\n\u003c/p\u003e\n\nPill adds dynamic content loading to static sites and makes content loading\nsmooth for users. It's pretty small only _1.45 KiB_ minified and gzipped. It fits perfectly\nfor static sites with WebComponents.\n\n* 🐥 **Tiny**: `1.45 KiB` gzipped.\n* 🥤 **Easy-to-use**: single function call.\n* 🎮 **Handful**: hooks and callbacks could modify the default behavior.\n\n\u003e Pill development started with the [tweet](https://twitter.com/sitnikcode/status/1109626507331338240)\nby Andrey Sitnik [@ai](https://github.com/ai).\n\nHow pill works. It:\n\n1. Intercepts navigation attempts: links clicks and history navigation.\n2. Loads requested url using `fetch`.\n3. Grabs content from received HTML.\n4. Replaces current page content.\n\nInitialize in one line:\n```javascript\npill('#content') // Yep, that's it.\n```\n\n## Table of Contents\n\n* [Install](#install)\n* [Usage](#usage)\n* [Corner cases](#corner-cases)\n* [API](#api)\n* [License](#license)\n\n## Install\n\n* Include script from unpkg.com:\n  ```html\n  \u003cscript src=\"https://unpkg.com/pill@1/dist/pill.min.js\"\u003e\u003c/script\u003e\n  ```\n\n  \u003e ⚠️ Remember about security! Add [subresource integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) (SRI) checksum\n  \u003e from [checksum.txt](https://unpkg.com/pill@1/dist/checksum.txt).\n\n* Install via npm:\n\n  ```\n  npm i pill\n  ```\n\n## Usage\n\n1. Inject pill's `\u003cscript\u003e` into page.\n2. Create content root element and give it id.\n3. Create loading indicator element.\n4. Initialize pill:\n  ```javascript\n  // Get loading indicator element\n  const indicator = document.querySelector('#indicator')\n  // Assign Pill to specified selector\n  pill('#content', {\n    onLoading() {\n      // Show loading indicator\n      indicator.style.display = 'initial'\n    },\n    onReady() {\n      // Hide loading indicator\n      indicator.style.display = 'none'\n    }\n  })\n  ```\n\n### Complete example\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eHome\u003c/title\u003e\n    \u003cscript src=\"https://unpkg.com/pill@1/dist/pill.min.js\"\u003e\u003c/script\u003e\n    \u003cstyle\u003e\n      /* global styles */\n      #indicator {\n        position: fixed;\n        top: 0;\n        right: 0;\n        display: none;\n      }\n    \u003c/style\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"indicator\"\u003eLoading...\u003c/div\u003e\n    \u003cdiv id=\"content\"\u003e\n      \u003cstyle\u003e/* page styles */\u003c/style\u003e\n\n      \u003c!-- page content here --\u003e\n    \u003c/div\u003e\n    \u003cscript\u003e\n      const indicator = document.querySelector('#indicator')\n\n      pill('#content', {\n        onLoading() {\n          // Show loading indicator\n          indicator.style.display = 'initial'\n        },\n        onReady() {\n          // Hide loading indicator\n          indicator.style.display = 'none'\n        }\n      })\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nEach document of the site should surround `#content` element with the same HTML.\nAll page-related content should be located inside `#content`. It could be styles, scripts, etc.\n\n## Corner Cases\n\n### No script inside of the content element\n\nScript elements placed inside of your content element wouldn't be evaluated after loading.\nYou should place all scripts out of your content element (in the `head` or `body`) and run JS manually.\nThis behavior prevents your site from memory licks and race conditions caused by inner scripts\ndifferent lifetime. And then you can react on page change with `onReady` hook to change your\napp behavior.\n\nExample:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"content\"\u003e\u003c/div\u003e\n    \u003c!-- common scripts --\u003e\n    \u003cscript src=\"/scripts/pill.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"/scripts/app.js\"\u003e\u003c/script\u003e\n    \u003cscript\u003e\n      pill('#content', {\n          onMounting(page, url, element) {\n            // Init page, for example bind event listeners, start timers, etc.\n            App.initPage(url, element)\n          },\n          onUnmounting(page, url, element) {\n            // Uninitialise page, for example remove event listeners, stop timers, etc.\n            App.destroyPage(url, element)\n          },\n       })\n       App.initPage(new URL(window.location), document.querySelector('#content'))\n    \u003c/script\u003e\n\u003c/html\u003e\n```\n\n## API\n\n### `pill()`\n```text\n(selector:string, options:PillOptions) -\u003e void\n```\n\nInitialize pill. Start listening for navigation attempts and history state changes. Puts loaded\ncontent into `selector` element.\n\n### Events\n\nYou can handle Pill's events by binding handlers on document element:\n\n```js\ndocument.addEventListener('pill:loading', (e) =\u003e {\n  e.detail.page; // Current page\n})\n```\n\n#### `pill:error` Event\n\n```text\n{\n  detail: {\n    url: URL\n    element: HTMLElement\n    error: Error\n  }\n}\n```\n\nIs emitted when the new page loading has been started. This event wouldn't be emitted\nif page is cached.\n\nCould be replaced with [PillOptions.onLoading()](#pilloptionsonloading) hook.\n\n#### `pill:loading` Event\n\n```text\n{\n  detail: {\n    url: URL\n    element: HTMLElement\n  }\n}\n```\n\nIs emitted when the new page loading has been started. This event wouldn't be emitted\nif page is cached.\n\nCould be replaced with [PillOptions.onLoading()](#pilloptionsonloading) hook.\n\n#### `pill:mounting` Event\n\n```text\n{\n  detail: {\n    page: Page\n    url: URL\n    element: HTMLElement\n  }\n}\n```\n\nIs emitted when new page content is about to be added into the DOM.\n\nCould be replaced with [PillOptions.onMounting()](#pilloptionsonmounting) hook.\n\n#### `pill:ready` Event\n\n```text\n{\n  detail: {\n    page: Page\n    url: URL\n    element: HTMLElement\n  }\n}\n```\n\nIs emitted when the requested page is mounted into DOM and no futher work would be done.\n\nCould be replaced with [PillOptions.onReady()](#pilloptionsonready) hook.\n\n#### `pill:unmounting` Event\n\n```text\n{\n  detail: {\n    page: Page\n    url: URL\n    element: HTMLElement\n  }\n}\n```\n\nIs emitted when new page content is about to be removed from the DOM.\n\nCould be replaced with [PillOptions.onMounting()](#pilloptionsonunmounting) hook.\n\n### Hooks\n\n#### `PillOptions.onError()`\n```text\n(error) -\u003e void\n```\nHandle page loading exception. By default is `console.error`.\n\n#### `PillOptions.onLoading()`\n```text\n(page:Page) -\u003e void\n```\nHandle loading start.\n\nCould be replaced with [`pill:loading` Event](#pillloading-event) listener.\n\n#### `PillOptions.onMounting()`\n```text\n(page:Page, url:URL, element:HTMLElement) -\u003e void\n```\nFires everytime new content is about to be loaded to the DOM.\n\nCould be replaced with [`pill:mounting` Event](#pillmounting-event) listener.\n\n#### `PillOptions.onReady()`\n```text\n(page:Page) -\u003e void\n```\nHandle loading finish.\n\nCould be replaced with [`pill:ready` Event](#pillready-event) listener.\n\n#### `PillOptions.onUnmounting()`\n```text\n(page:Page, url:URL, element:HTMLElement) -\u003e void\n```\nFires everytime content is about to be removed from the DOM.\n\nCould be replaced with [`pill:unmounting` Event](#pillunmounting-event) listener.\n\n### Other options\n\n### `PillOptions.fromError()`\n```text\n(error:Error) -\u003e {title, content}\n```\nUse it to display notification when something went wrong.\nIf an error was thrown while handling request. You still able\nto render content using method `fromError`\n\n### `PillOptions.getKeyFromUrl()`\n```text\n(url:URL) -\u003e String\n```\n\nGet cache key from URL. It's useful when URL contains query params which\nare unknown to server and could not affect response. By default any\nnew pathname and search string combination will cause new request.\n\n### `PillOptions.shouldReload()`\n```text\n(page:Page) -\u003e Boolean\n```\n\nDetermine wether previously loaded page should be loaded from server.\n\n### `PillOptions.shouldServe()`\n```text\n(url:URL, target:HTMLElement) -\u003e Boolean\n```\nDeveloper-defined logic to determine whether the URL could be served by Pill.\nIf you return `false` then the link will be served by browser.\n\n## License\n\nMIT © [Rumkin](https://rumk.in)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frumkin%2Fpill","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frumkin%2Fpill","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frumkin%2Fpill/lists"}