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

hairdresser

v0.3.1

Published

A universal js library for managing head DOM elements (title, link, and meta)

Downloads

20

Readme

A universal js library for managing head DOM elements (title, link and meta)

Browser support includes every sane browser and IE8+. (IE8 with es5-shim)

NPM Version Linux Build Windows Build Coverage Status

Sauce Test Status

var hairdresser = Hairdresser.create();
hairdresser.override()
  .title('Hairdresser')
  .meta({ property: 'og:title' }, { content: 'Hairdresser' });

hairdresser.render();

Features

  • Manages <head>'s child elements (ex. <title>, <meta> and <link>)
  • Hierarchical state friendly API
  • Server-side rendering

Motivation

Independence from framework

There are already good projects for managing <title>, <meta> and <link> elements. Following are some of the projects:

Most of the projects depend on client-side frameworks like AngularJS or ReactJS. Using frameworks can make code shorter and simpler, but it also costs flexibility and maintainability. I think the cost exceeds the benefit. There are good framework independent libraries for sortable UI and drag and drop. Why not for <head> manipulation?

Hierarchical state friendly API

As <head>'s child elements represent state of the app, <head> manager should receive state change events and change <head> rendering function properly. For example, an app uses app name for <title> on root state, and uses title of the article for <title> on article showing state.

In most cases, app uses hierarchical state like this:

Root > Category > Sub category > Article List

Hierarchical architecture is popular (ex. file system), and can take advantage of URL's hierarchical representation. In fact, the strength of hierarchical state is not important. The thing is, hierarchical state is common for web apps and <head> manager should offer hierarchical state friendly API.

How to use

1. Get started with default values

Let's start by creating a Hairdresser instance.

// With new operator
var hairdresser = new Hairdresser();

// Or with static function
var hairdresser = Hairdresser.create();

Hairdresser manages one controller per DOM element in an override fashion. A controller defines when and how to render an element. To set default values, just call hairdresser.override and pass render parameter per element. render parameter is the first parameter of title method and the second parameter of meta and link methods.

hairdresser.override()
  .title('Hairdresser')
  .meta({ name: 'title' }, { content: 'Hairdresser' })
  .link({ rel: 'canonical' }, { href: 'https://example.com' });

This is <head> inner HTML that the above code expects.

<!-- Alphabetical order -->
<link rel="canonical" href="https://example.com"></meta>
<meta name="title" content="Hairdresser"></meta>
<title>Hairdresser</title>

render parameter can be a value which replaces element content or a function that returns the element replacing value. For example, following two examples change element in the same way.

// Example1 - Use static primitive string value.
hairdresser.override()
  .title('Hairdresser');

// Example2 - Use dynamic function return value.
hairdresser.override()
  .title(function () {
    return 'Hairdresser';
  });

As for <title>, there is no need of a selector because only one <title> can exist. render parameter of <title> should be or return a string value because title is string.

In case of <meta> and <link>, there can be multiple elements with the same tag name in DOM, so a selector is required. The first argument of meta and link function is the selector, which represents key-value attribute pairs. Similarly, render parameter of <meta> and <link> should be or return an object as key-value attribute pairs.

Now we should call hairdresser.render in order to make Hairdresser start manipulating DOM.

hairdresser.render();

2. Override controllers on state transition

When application state changes, you may want to change a controller to handle an element. For example, you want to set <title> to the default value on the main page, and want to set <title> to article title on article page. Then, you can override the controller for <title> like this:

var article = {
  title: 'My awesome article'
};

var override = hairdresser.override()
  .title(function () {
    return article.title;
  });

// Cancel override on leaving the article page.
onLeaveState(function () {
  override.restore();
});

Now <title> is set to article's title, My awesome article. You can go back to the previous controller by calling restore method.

In case of hierarchical state, you can override a controller multiple times when you traverse down multiple child states.

3. Trigger update

You can update <head> element in two ways.

One is manual function call. override.update updates all the elements in override.

var override = hairdresser.override()
  .title(getTitle);

onDataReceived(function () {

  // Manual update
  override.update();
});

The other one is using event listeners. A listener can be added to override and each element.

Many event emitter's listener adding functions return listener removing function. For those cases, to manage the removing function in a simple way, the return value of addListener is passed to removeListener as the second parameter.

// Use fbemitter (https://github.com/facebook/emitter)
var emitter = new EventEmitter();

// Listener per override
var override = hairdresser.override({
  addListener: function (listener) {
    return emitter.addListener('update.override', listener);
  },
  removeListener: function (listener, token) {
    token.remove();
  },
}).title(getTitle);

emitter.emit('update.override');

// Listener per element
var override = hairdresser.override()
  .title(getTitle, {
    addListener: function (listener) {
      return emitter.addListener('update.title', listener);
    },
    removeListener: function (listener, token) {
      token.remove();
    });
  });

emitter.emit('update.title');

4. Render to DOM or string

This library is universal. You can modify <head> DOM element on browser.

var hairdresser = Hairdresser.create();
hairdresser.override().title('Hairdresser');

// render() will render `<head>` DOM element,
// and watch further overrides.
hairdresser.render();

You can also get elements as a string value.

var hairdresser = Hairdresser.create();
hairdresser.override().title('Hairdresser');

var head = hairdresser.renderToString();
assert(head === '<title>Hairdresser</title>');

API

Refer to API documentation

Roadmap

Refer to Roadmap documentation

Examples

License

MIT