How it Works

In order to allow for the recognition and parsing of custom HTML tags, X-Tag relies on a variety of events to detect and upgrade elements with user-defined, extended functionality. X-Tag works regardless of whether the element was present in the original source of the document, added by setting innerHTML, or generated dynamically using document.createElement. Here's an example showing the registration of a custom element, with a description of each of the foundational interfaces the API offers:

xtag.register('x-accordion', { // extend existing elements extends: 'div', lifecycle:{ created: function(){ // fired once at the time a component // is initially created or parsed }, inserted: function(){ // fired each time a component // is inserted into the DOM }, removed: function(){ // fired each time an element // is removed from DOM }, attributeChanged: function(){ // fired when attributes are set } }, events: { 'click:delegate(x-toggler)': function(){ // activate a clicked toggler } }, accessors: { 'togglers': { get: function(){ // return all toggler children }, set: function(value){ // set the toggler children } } }, methods: { nextToggler: function(){ // activate the next toggler }, previousToggler: function(){ // activate the previous toggler } } });
Custom Tag Registration


Whenever a tag is recognized and parsed on load or generated using document.createElement, a created function is called allowing you to modify the element before any other code is applied to it.

xtag.register('x-superinput', { lifecycle: { created: function(){ // superinputs begin life knowing they're super. this.value = 'super'; } } });


The inserted method is called everytime a given component's DOM element is added to the DOM. This allows you to do things like check the state or structure of the surrounding DOM tree or window scope and modify your component accordingly.

xtag.register('x-superinput', { lifecycle: { inserted: function(){ // superinputs announce their arrival in the DOM like a boss! alert("Yeah, that's right, superinput comin' thro'!"); } } });


The accessors object provides native value retrieval handlers to your component. For example: if a user caches a component's value, var value = element.value; your getter would have the opportunity to fetch that value from anywhere you'd like and even modify it before returning.

xtag.register('x-superinput', { accessors: { value: { get: function(){ // everything superinputs do has a little super mixed in ;) return this.dataset.value + ' is super'; } } } });


The attribute object allows easy mapping from an accessor to an element attribute. Instead of having to write getAttribute and setAttribute inside of your accessors, you can add the attribute object to create the mapping automatically. There are also a few options available inside the attribute object. See the code example below.

xtag.register('x-superinput', { accessors: { enabled: { attribute: { boolean: true } // creates a boolean attribute. enabled="" }, lastName: { attribute: { name:'last-name' } // use a different attribute name } } });


The events object allows you to bind events to the component at the time of creation. Pseudo events like delegation are supported, additional pseudos can be added to the pseudos object on the global xtag variable.

xtag.register('x-superinput', { events: { focus: function(){ // what should superinputs do when they are in the spotlight? } } });


The mixins array allows you specify mixin keys that map to collections of getters, setters, events, and lifecycle functions. X-Tag merges mixins into your component definition object for you. Mixins are found on the mixins object of the global xtag variable - you can add your own mixins there too!

xtag.register('x-superinput', { mixins: ['superdefaults'] });

extendselement name

Extends allows you to use an existing html element as the base of your custom element. Common values are div, span, input.

xtag.register('x-superinput', { extends: 'input' });


Prototype allows you to set the prototype of the custom element to any object you wish. This enables you to incorporate functionality of another object into your custom element.

xtag.register('x-supertemplate', { prototype: Object.create(HTMLDivElement.prototype) });

The X-Tag library comes with just enough helper methods to make it bearable to work in Vanilla JS.


Converts the given object into an array.

hasClasselement, className

Returns a boolean that indicates if the element has the specified class.

addClasselement, className

Adds a class to the element.

removeClasselement, className

Removes a class from the element.

toggleClasselement, className

Adds the class if it doesn't exist on an element or removes the class if it exists.

matchSelectorelement, selector

Returns a boolean that indicates if the given selector matches the element.

if(xtag.matchSelector(element, '.foo')){ // matches }else{ // doesn't match }

queryelement, selector

Runs querySelector all on the given element and returns the results as an array.

xtag.query(element, '.foo').forEach(function(elem){ // iterate over matches });

queryChildrenelement, selector

Allows you to query only the direct children of the element.


Returns an animation frame.

createFragmentelement or html string

Creates a document fragment out of the passed element.

wrapfunction, function

Returns a new function where the first function is called, then the second function. If false is returned from the first function then the second function will not execute.

innerHTMLelement, html

Sets the innerHTML of element with the passed html and parses for x-tags. You need to do this instead of just assigning the innerHTML property if you intend to use custom elements as part of the html of a custom element.

xtag.register('x-foo', { lifecycle: { created: function() { // works because we're not using another component this.innerHTML = 'Hello foo'; } }, methods: { hello: function() { console.log('hello from x-foo'); } } }); xtag.register('x-bar', { lifecycle: { created: function() { // Since we use xtag.innerHTML, x-foo will be available immediately xtag.innerHTML(this, 'Super <x-foo></x-foo>'); // You can call x-foo methods already var foo = this.querySelector('x-foo'); foo.hello(); } } });

fireEventelement, eventType, object

Creates a DOM custom event and fires it on the given element.

xtag.fireEvent(element, 'beep'); xtag.fireEvent(element, 'beep', { bubbles: false, cancelable: true, detail: {foo: 'bar'} });

addEventelement, eventType, function

Adds a DOM event listener to an element. It also allows for event pseudo chains. Returns an event object that is used for removal of the event listener.

var event = xtag.addEvent(element, 'click:delegate(button)', function(e){ // "this" is the button element });

addEventselement, object

Adds multiple DOM event listeners to an element. Returns a collection of event objects that is used for removal of the event listeners.

var events = xtag.addEvents(element, { 'click:delegate(': function(){ }, 'click:delegate(button.cancel)': function(){ } });

removeEventelement, eventType, event

Removes a DOM event listener from an element. Passing the event type is optional.

// Obtaining the event object var event = xtag.addEvent(element, 'click', function (e) { … }); // Removing the event listener xtag.removeEvent(element, 'click', event);

removeEventselement, events

Removes multiple DOM event listener from an element.

// Obtaining the events object var events = xtag.addEvents(element, { … }); // Removing the event listeners xtag.removeEvents(element, events);

X-Tag pseudos can be applied to accessors, events and methods. They allow you to easily tack on common functionality like filtering and delegation.


Delegate works by attaching a listener to a given element/document and will call the provided function if the target matches the delegate selector.

// Example 1 // Listen on the document for any button with a class of 'save' xtag.addEvent(document, 'click:delegate(', function(e){ // <button class="save"> was clicked // 'this' is the button }); // Example 2 // Here's the syntax inside a custom element. xtag.register('x-foo', { lifecycle: { created: function(){ this.innerHTML = '<a>HI</a>'; } }, events: { 'click:delegate(a)': function(e){ console.log('a clicked', this); } } });


Keypass acts as a filter, only letting through certain keycodes.

xtag.addEvent(input, 'keydown:keypass(13)', function(e){ // 'enter' pressed }); xtag.addEvent(input, 'keydown:keypass(13,27)', function(e){ // 'enter' or 'esc' pressed });

Anything placed inside a template element will be turned into a HTML document fragment. Use the .content accessor to access the document fragment and cloneNode() to create a new instance of the contents.

<template id="test-template"> <div>test</div> </template> var t = document.getElementById('test-template'); document.body.appendChild(t.content.cloneNode());

You can also use templates when registering components. For example:

<template id="test-template"> <div>test</div> </template> xtag.register('templated-component', { lifecycle: { created: function() { var tpl = document.getElementById('test-template').content; this.appendChild(tpl.cloneNode(true)); } } }); var t = document.createElement('templated-component'); document.body.appendChild(t);