tagr
v0.0.8
Published
A portable DOM for the web and Node.js.
Downloads
35
Readme
tagr
Solid HTML manipulation for web apps.
The DOM is a messy place. Tagr is a concise interface to the DOM: work with only elements and strings, and manipulate their events, styles, and properties. Tagr is not a "rapid" web development library, but instead a solid foundation for manipulating HTML with a consistent API on which other tools can be built.
Tagr's biggest advantage is portability: Like a <canvas>
context, tagr creates HTML context where all styles and events are relative and sandboxed. Documents can be built procedurally, or built with JSON or HTML. You can even construct entire widgets on the server and send them to the client, serializing them as HTML to take advantage of fast parsing times.
Tagr is a small library, minified (15kb) and gzipped (5kb). Supports IE6+, Firefox 3.5+, Chrome, Safari, and Opera.
Works well with...
- Sizzle — Including this adds support for complex CSS3 queries using the Sizzle engine.
- selection.js — Including this enables the Selection API.
- store.js by marcuswestin — Easy client-side storage.
Getting started
Elements are instantiated by tagr.create()
, and text are simple strings.
> var div = tagr.create('div.alert');
> div.push('Hello world!')
> div.toHTML()
<div class="alert">Hello world!</div>
You can manipulate or index children like an array:
> div[0]
"Hello world!"
> div.splice(0, 1, 'Such ', tagr.create('b', {}, 'bold'), ' moves!')
> div.toJSON()
['div', {}, 'Such ', ['b', {}, 'bold'], ' moves!']
Attach elements to a document by creating a context, from an existing element or writing one directly:
> tagr.getContext(document.body)
> tagr.writeContext('div#contents', {}, 'Great for inline widgets!')
API Reference
tagr
module
You can create elements with the method tagr.create()
, or parse a JSON tree or HTML with tagr.parse()
to return a TagrElement
.
- tagr.create([tag[, properties[, children...]]) returns TagrElement — Create a new
TagrElement
with the given tag and properties, and optionally children. - tagr.parse(json) returns TagrElement — Parses a JSON structure or HTML string into a
TagrElement
.
Your entry point to the DOM is a Tagr "context". The DOM inside a context is mirrored by Tagr and must only be modified with Tagr commands. You can either load an existing element and its children, or write one into the DOM directly:
- tagr.getContext(node) returns TagrElement — Get a Tagr context for the given DOM node.
- tagr.writeContext(tag[, properties[, children...]]) returns TagrElement —
document.write
aTagrElement
and immediately return it. Useful for self-contained scripts.
And the obligatory DOMContentLoaded polyfill:
- tagr.ready(callback) — Trigger the given callback once the DOM is (or has already) finished loading.
TagrElement
class
A Tagr element object can be created using tagr.create()
or parsing JSON or HTML using tagr.parse()
. It has the following read-only properties:
- el.tag
- el.id
- el.parent
Tagr includes a classList
property, similar to the DOM property, which lets you add or remove classes easily.
- el.classList.contains(key)
- el.classList.add(key)
- el.classList.remove(key)
- el.classList.toggle(key)
Element properties
Tagr supports modifying built-in DOM properties using .get(key)
and .set(key, value)
methods, similar to the Backbone.js API.
- el.get(key)
- el.set(key, value | properties) returns
el
- el.remove(key)
- el.call(key, arguments...)
- el.properties() returns
map
When a property is set on an element, a change:[property name]
event is emitted on the element. This includes when the user modifies an input field, so .on('change:value', function () { ... })
works as advertised for inputs and textareas!
Only properties and attributes that are unique to an element are exposed. For instance, you can modify the href
of a link or the currentTime
of a video, but parentNode
and other DOM manipulation methods are not exposed. In this way, you can leverage the HTML JavaScript API without inadvertantly modifying the DOM.
Custom properties are allowed on elements, and provide a powerful way to create widgets:
> ctx.query('.progress').on('change:level', function () { /* set progress bar level */ })
> var prog = tagr.create('.progress', {level: 50})
> prog.set('level', 75) // etc
Custom properties are kept separate from built-in HTML attributes, and are serialized as data-*
attributes to HTML and JSON. See Serialization below.
Element styles
You can manipulate an element's styles and individual style rules simply:
- el.style(key, value | styles) returns
el
- el.addRule(key, value)
- el.removeRule(key)
- el.rules() returns
map
Element events
Tagr elements support the familiar EventEmitter API from Node.js. All listeners are attached to the DOM element themselves:
- el.on(type, callback) returns
el
- el.once(type, callback) returns
el
- el.addListener(type, callback) returns
el
- el.removeListener(type)
- el.listeners([type])
- el.removeAllListeners()
- el.emit(type, args...)
- el.setMaxListeners(count)
Note that Tagr does not have a mechanism to propagate an event to itself and its ancestors. (Though maybe it should.)
Element children
Tagr elements act like arrays, and inherit the array prototype. You can iterate over them by index, though array access is read-only:
- el.length
- el[0...length]
Tagr elements implement array methods to manipulate and all elements and text in-place. You can pass in other tagr elements or raw strings as children. Whenever an element is attached to a parent, an attached
event is fired on the element, and vice versa for detached
. This can be used to dynamically enable behaviors for elements.
These methods manipulate an element's children:
- el.push(children...)
- el.pop() returns
object
- el.unshift(children...)
- el.shift() returns
object
- el.splice(index, deleteCount, insert...)
- el.sort([compare])
- el.reverse()
In addition, Tagr provides all ES5 array utility methods and supports slice
, sort
, reverse
, indexOf
, forEach
, map
, every
, lastIndexOf
, filter
, some
, reduce
, and reduceRight
. Note that arrays returned by these methods are not instances of TagrElement
.
Tagr also provides its own chainable methods to manipulate an element's children in-place:
- el.insert(index, children...) returns
el
— Inserts the children at the given index. The index can be negative; -1 inserts at the end of the array. - el.remove(index[, count]) returns
el
— Removes children at the given index. The index can be negative; -1 removes from the end of the array. - el.children(_children...) returns
el
— Overwrites all children with the given list.
As well as methods to manipulate a child itself:
- el.insertSelf(parent, index) returns
el
- el.removeSelf() returns
el
- el.indexOfSelf() returns
el
Since text nodes are represented in Tagr as regular strings, Tagr elements provide methods to split or splice text directly:
- el.spliceText(index, spliceIndex, deleteCount, insertString)
- el.splitText(index, splitIndex)
Relative queries
A TagrQuery
can be generated relative to any Tagr element:
- el.query(selector) returns TagrQuery
See the API for TagrQuery
below. As an additional shorthand on elements, you can directly request the first or an array of elements matching a query selector.
- el.find(selector) returns TagrElement — Shorthand for
el.select(selector).findAll()
. - el.findAll(selector) returns [TagrElement] — Shorthand for
el.select(selector).findAll()
.
Serialization
A TagrElement
can be serialized to valid JSON or HTML. (The preferred serialization format is JSON.) Either format can be reconstructed as a tree using tagr.parse()
.
- el.toJSON() returns Object — Returns a JSONML formatted tree.
- el.toHTML() returns String
By default, properties are not persisted as JSON or HTML unless they are built-in element attributes (like value
, src
, rowspan
, etc.) You can define that custom attributes be persisted or HTML elements should not be persisted by specifying them manually:
- el.persist(name)
- el.stopPersisting(name)
If a custom property is persisted, it is serialized to JSON and set as a data attribute (prefixed with data-
). These attributes are properly read by tagr.parse()
and the data- prefix is removed.
Convenience
Tagr is for web apps, so a few convenience functions are included to get you started:
- el.useWhitespace(toggle = true) — Sets that whitespace should be significant. Since you aren't dealing with a markup language, you can have your non-breaking cake and eat it, too.
- el.setSelectable(toggle = true) — Sets that element text should not be selectable by the cursor.
TagrQuery
class
A Tagr query encapsulates a base element and a selector to match elements below it. A query can be created using TagrElement::query()
, and the query contains a reference to the base element:
- query.base — The Element context for this TagrQuery.
Many of the same actions you can perform on a TagrElement
can be done for a dynamic query. You can listen for and emit events, or create styles and rules:
> ctx.query('.tooltip').on('mouseover', function () { /* display tooltip */ })
> ctx.query('.btn').style({'background': 'blue'})
Additionally, at any point you can return a static array of elements which match the query at the time it was invoked:
- query.find() returns TagrElement
- query.findAll() returns [TagrElement]
tagr.view
namespace
(This is a work in progress.)
- tagr.view.getBox(element[, relativeTo]) returns Rectangle — Get the bounding box of the element, with properties
width
,height
,top
,right
,bottom
,left
. If an element is provided forrelativeTo
, these coordinates are relative to this other element. - tagr.view.getStyle(element, key) — Gets the computed CSS value for
key
ofelement
.
The selection API is available when selection.js is included. All elements are TagrElements:
- tagr.view.selection.has(window)
- tagr.view.selection.getOrigin(window)
- tagr.view.selection.getFocus(window)
- tagr.view.selection.getStart(window)
- tagr.view.selection.getEnd(window)
- tagr.view.selection.set(window, origin, focus)
License
MIT.