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)
var hairdresser = Hairdresser.create();
hairdresser.override()
.title('Hairdresser')
.meta({ property: 'og:title' }, { content: 'Hairdresser' });
hairdresser.render();
- Manages
<head>
's child elements (ex.<title>
,<meta>
and<link>
) - Hierarchical state friendly API
- Server-side rendering
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?
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.
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();
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.
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');
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>');
Refer to API documentation
Refer to Roadmap documentation
MIT