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

content-handler

v1.1.5

Published

An unified system to easily handle all your HTML/SVG contents, using simple event listeners

Downloads

20

Readme

content-handler

Based on EventTarget, content handlers are an unified system to automate and simply await some DOM (HTML/SVG) contents from the server, ignoring the content origin, avoiding complex logic in your front code, even from AJAX requests or received as Server-Sent Events

Install

npm i content-handler

Quick start

Create your own content-handler based project to build your app/site in seconds!

Browsers support

How a content handler gets the contents?

  1. Full page loading
  2. Register your listeners, based on selector events (including your controllers)
  3. The ContentHandler triggers the listeners on the matching elements
  4. Your controllers are awaiting some user events (click on an anchor, submit on a form)
  5. The ContentSupervisor triggers the related controllers
  6. Fetching
  7. The ContentHandler parses the response in a DocumentFragment and returns to step 3.

Get the content handler for a document

Each handler handles a single document!

import ContentHandler from 'content-handler'

// If the document isn't provided, it uses the current window.document
const handler = ContentHandler.getByDocument(document)

Await elements from a handler

Loaded or not

import ContentHandler from 'content-handler'

// Await any main tag, already present in the document or loaded by the content
// handler, to apply the listener on it
ContentHandler
  .getByDocument()
  .addEventListener('main', ({element}) => {
    // Rewrites the content (just to illustrate, don't do that in real projects)
    element.innerHTML = `<h1>You're currently testing content-handler</h1>
    <p>This main is rewritten by the ContentHandler's listener, even if loaded
    or not, but in this case, this node <em>is already contained</em> by the
    document, at the page loading.</p>`
  })

Loaded only

The fetched elements are not in the current page, they need to be added on it.

import ContentHandler from 'content-handler'

// Await any main tag, but only loaded by the content handler, to apply the
// listener on it
ContentHandler
  .getByDocument()
  .addEventListener('body:first-child main', ({element}) => {
    // Select the current main
    const main = document.querySelector('main')
    
    // Replace the current main by the new one
    main.parentNode.replaceChild(element, main)
  })

Present only

The fetched elements are already in the current page, they don't need to be added on it.

import ContentHandler from 'content-handler'

// Await any main tag, already present in the document, to apply the listener on it
ContentHandler
  .getByDocument()
  .addEventListener('body:not(:first-child) main', ({element}) => {
    // Rewrites the content (just to illustrate, don't do that in real projects)
    element.innerHTML = `<h1>You're currently testing content-handler</h1>
    <p>This main is rewritten by the ContentHandler's listener, but only if this
    node <em>is already contained</em> by the document, at the page loading.</p>`
  })

Declare the controllers

A content handler doesn't make anything alone, to automate the fetching ou SSE listening, you need to tell him how to do it.

It also uses the handler.addEventListener() and should need some controllers (provided or custom) to define the request behavior, make some pre-validation, some client optimizations before to send it to the server, etc.

The provided controllers are generic as possible, not required but recommended, to avoid uncommon behaviors between browsers.

It shipped with 3 simple initial controllers with, for each, a default .selector to target the related elements (you can define yours), and a .listen() method to control the request.

anchor

import ContentHandler from 'content-handler/content-handler.js'
import anchor from 'content-handler/controllers/fetcher/anchor.js'
import cache from 'content-handler/controllers/fetcher/init/cache.js'
import headers from 'content-handler/controllers/fetcher/init/headers.js'
import credentials from 'content-handler/controllers/fetcher/init/credentials.js'
import mode from 'content-handler/controllers/fetcher/init/mode.js'
import redirect from 'content-handler/controllers/fetcher/init/redirect.js'
import referrer from 'content-handler/controllers/fetcher/init/referrer.js'

ContentHandler
  .getByDocument()
  .addEventListener(anchor.selector, anchor.listen([
    cache.default, // follow the default cache rule
    headers.xhr, // add the common AJAX header
    credentials.sameOrigin, // allow credentials only for the current origin
    mode.sameOrigin, // allow requests handling only for the current origin
    redirect.follow, // follows the redirects
    referrer.client // set request.referrer to "about:client" by default
], {/* optional env object */}))

form

import ContentHandler from 'content-handler/content-handler.js'
import form from 'content-handler/controllers/fetcher/form.js'
import cache from 'content-handler/controllers/fetcher/init/cache.js'
import headers from 'content-handler/controllers/fetcher/init/headers.js'
import credentials from 'content-handler/controllers/fetcher/init/credentials.js'
import mode from 'content-handler/controllers/fetcher/init/mode.js'
import redirect from 'content-handler/controllers/fetcher/init/redirect.js'
import referrer from 'content-handler/controllers/fetcher/init/referrer.js'

ContentHandler
  .getByDocument()
  .addEventListener(form.selector, form.listen([
    headers.contentType, // detect the content type, if needed
    cache.default, // follow the default cache rule
    headers.xhr, // add the common AJAX header
    credentials.sameOrigin, // allow credentials only for the current origin
    mode.sameOrigin, // allow requests handling only for the current origin
    redirect.follow, // follows the redirects
    referrer.client // set request.referrer to "about:client" by default
], {/* optional env object */}))

sse

import ContentHandler from 'content-handler/content-handler.js'
import sse from 'content-handler/controllers/sse/sse.js'
import withCredentials from 'content-handler/controllers/sse/configuration/with-credentials.js'
import input from 'content-handler/controllers/sse/input.js'

ContentHandler
  .getByDocument()
  .addEventListener(sse.selector, sse.listen([
    input.dataset, // get the input url from "data-sse" attribute
    withCredentials.sameOrigin // allow credentials only for the current origin
  ], {/* optional env object */}))

Make your own controller

Making your own controller is really easy, it just a function, receiving a request config object and returning a new one based on it.

As a best practice, you never should modify the received object!

For asynchronous operations, in place of returning the new config object, you can simply return a Promise, resolving the config object.

The unique difference between AJAX/SSE config objects is the init/configuration property

function customFetcherController (config) {
  const {input, supervisor} = config // {element, init, input, supervisor, env}
  
  // if the request doesn't targets the current origin, abort
  if (input.origin !== document.location.origin) {
    supervisor.abort()
  }
  
  return {...config}
}
function customSSEController (config) {
  const {input, supervisor} = config // {configuration, element, input, supervisor, env}
  
  // if the request doesn't targets the current origin, abort
  if (input.origin !== document.location.origin) {
    supervisor.abort()
  }
  
  return {...config}
}

License

MIT