html domt复制,GitHub - kapouer/domt: Serializable DOM templating

domt -- simple DOM Templating tool

Merge plain javascript objects into DOM.

Support following workflow :

add template attributes like bind, repeat, or [acc.sess.ors] to static

html document

load document, merge data into it using those attributes and fields

unload document (serialize it)

reload document later and merge new data

And stick with these constraints :

light (5.8KB minified)

simple

thorough test suites

force separation between logic (js), template (html), and data (obj)

pure js and DOM, cross browsers (well, hmm, mostly)

extensible with filters

Quick start

Starting with a template and a call to Domt:

  • first item

Domt('#test').merge({

"class": "list",

items: [

{color: "blue", text: "the sea"},

{color: null, text: "the void"}

]

});

We get:

  • the sea
  • the void

The API is chainable and calling it a second time is possible:

Domt('#test').merge({

"class": "list"

}).merge({

items: [

{color: "blue", text: "the sea"},

{color: null, text: "the void"}

]

});

One can create another instance of Domt to update a previously merged node:

Domt('#test').merge({

"class": null

}).merge({

items: [

{color: "red", text: "another one"}

]

});

The second call with remove attribute "class" from #test, and append a

list item named "another one".

It is also possible to empty the repeated lists:

Domt('#test').empty().merge({

items: [

{color: "red", text: "another one"}

]

});

Domt accepts a jquery object

Domt($('div > .test').not('.out'))

Domt accepts a documentFragment

Domt(myTemplateNode.content).merge(data).nodes == myTemplateNode.content

though in this case one typically would prefer to clone the content fragment.

Attributes

bind

replace current context data with the data found by the accessor in the value.

If empty, used as a way to make sure the node will be merged.

bind-xxx

will create a xxx attribute (or merge it if already present)

Several attributes are search by default. To add more, just do

Domt.query.push('xxx') so that a node having a bind-xxx attribute will

be found if the only recognizable attribute is this one.

repeat

repeat-with

See below.

repeat-name

name a repeated fragment, so that Domt.template(name) return its template.

The fragment must have been merged once before.

Usage: Domt.template('myfrag').clone().merge(data).

Which DOM nodes are processed ?

Attributes "bind-*" that are not descendents of a node with a "repeat"

attribute are not processed unless they have a "bind" (empty or not)

attribute.

Example: Domt('body').merge({myval: 'test'}); won't merge the span.

Either call Domt on that span, or add a bind attribute to it.

[myval]

the target given as argument of Domt(target)

target is either a node, a list of nodes, or a selector expression

supported by document.querySelectorAll.

instance.nodes keeps the actual list.

the descendants with attribute "bind"

in itself the node doesn't change, the "bind" attributes set the current

base path for all accessors in the same node or its descendants.

A "bind" attribute is always processed before a "repeat" attribute when

they are on the same node.

For example , or

...
where lastTwo

is a value filter like function(val) { return val.slice(-2); }.

the descendants with attributes starting with "bind-"

The attribute value is an object accessor of the form "path.to.key",

and it can be empty (in which case parent object is used).

If a value is :

undefined or null, obj isn't changed

string, path is used as an accessor of obj

function, obj is the result of fun(obj, paths)

the descendants with attribute "repeat"

repeats the node (and its content) by iterating over accessed data.

If a value is :

undefined or null, obj isn't changed

string, path is used as an accessor of obj

function, obj is the result of fun(obj, paths)

Current item has two special properties: #key and #val.

Accessed data can be iterated if it is :

an array

an object with length property and .item(i) method

finally the object is looped as an hash array

the next siblings with attribute "repeat-with" of a node with attribute "repeat"

this allows repeating several nodes at once, as a fragment.

When doing this, the block filter context.node argument is a fragment.

Operations on instances

The actual data merging is controled by these attributes:

bind-/attributeName/

where attribute is the name of the attribute to process.

bind-text

node.innerHTML = escaped_text(value)

bind-html

node.innerHTML = value

If a value is :

undefined, expressions in the target are evaluated

null, target is removed

string, target is replaced by the value accessed in obj

function, the value is replaced by the result of fun(obj, paths)

When merged, a repeated node looses its bind and repeat attributes (but

doesn't remove attributes added by a "bind-bind-att" trick) and

non-repeated nodes keep their bind-* attributes.

Expressions are written as "[path.to.val|optional_filter]" and are

replaced by their accessed value in the target.

A node is repeated in natural order. To invert that order, just add a block

filter like 'invert'. A custom block filter can sort items in any desired order.

  • first item

Note that if node contains other nodes that are targets of Domt,

the usage of bind-html or bind-text on that parent node is not defined.

It is strongly advised to avoid that situation:

[data.text]

link

Instead, wrap the first text node in a span:

[data.text]

link

Global settings

Domt.ns object is used to set the prefixes used for the attribute names,

or change delimiters, like this: Domt.ns.expr = '{{*}}';.

It's also possible to change the "bind-" prefix and other names, just have

a look in Domt's code and tests to find out about Domt.ns.

Domt.ns.query holds a list of bind-* attributes to search for,

those special ones do not need "repeat" or "bind" to be set at all.

As for filters, query options can be passed in Domt(node, {query: ["size"]});,

which will search for nodes having a bind-size attribute.

Note that all attributes of a node are checked when there is one bind, repeat,

or bind-* attribute, so typically

bothe attributes are merged correctly.

Domt.filters stores filters prototype (shared by all domt.filters instances).

To add per-instance filters, use either

Domt(parent, {

myFilter: function(str) {

return '' + this.text(str) + '';

}

});

or

var inst = Domt(parent);

inst.filters.myFilter = function(str) {...};

or even

var inst = Domt(parent);

inst.merge(data, {

myBlockFilter: function(item, key, context) {...}

});

In a filter function, the first argument is always the data being merged,

the last argument an object giving some access to current context, see below.

Data getters

As mentioned above, merging a function will call it with parameters

(obj, paths). This cannot be used to modify the DOM, only the data.

Value Filters

Value filters are called before merging the value in the target.

A value filter is a simple function returning a string.

If the a value (returned by filter or not) is undefined, the target is not merged.

If the a value (returned by filter or not) is null, it is merged as empty string

but if it's an attribute it doesn't create it (use empty string for that).

Domt.filters.myfilter = function(val, context) {

// this will always merge something since it casts undefined to string

return "my" + val;

};

Where context contains {

node: the node where the value is being merged,

att: the name of the target attribute (it cannot be "text" or "html"), if any,

path: array of path components,

index: current index in array of path,

name: the last name of the path before this filter was called,

scope: the root object being accessed by path,

filters: the available filters

}

Value filters are not called if the first key in the accessor matches undefined

data. Any other case (> first key, null or empty data) will call the filter.

Some filters are already availables:

upper : toUpperCase

lower : toLowerCase

br : replace newlines by

, to be used with in bind-html.

text : escape html, the combination text|br is useful

esc : encodeURIComponent

unesc : decodeURIComponent

json : JSON.stringify

int : parseInt or ""

float : parseFloat or ""

att : return attribute name if current value evaluates to true

drop : remove target attribute if current value evaluates to true - does not

modify val.

always: return true

never : return false

! : return !val

? : return val.toString() if it's not empty, else return null or undefined or ""

log : log value and context path, name, index

More filters could be written, but they would typically depend on features

like Element.classList or jQuery - they're not fit to be in domt.

Note that escaping xml entities is usually not needed because we use the

DOM methods and they do the conversions for us.

Filters can accept objects as well, see test/replacement.html.

Block Filters

While value filters are declarable when merging values, a block filter can

be declared on the repeated accessor

  • [items.text]

Block filters are called after the current data has been merged into the

cloned node and before the cloned node is inserted into the list.

Domt.filters.myBlockFilter = function(row, key, context) {

if (row.selected) node.selected = true;

// node can be inserted manually, sibling holds the node before which it would

// be inserted by default

context.tail.parentNode.insertBefore(context.node, context.tail); // default insert

// can be replaced by

if (row.invalid) {

// prevent context.node from being inserted into the list

return false;

} else {

var oldnode = document.getElementById(row.nodeid);

oldnode.parentNode.replaceChild(context.node, oldnode);

}

};

Where context contains {

node: the documentFragment (or the only child node of it) being merged,

parent: the parent where the node will be inserted by default,

path: array of path components,

index: current index in array of path,

name: the last name of the path before this filter was called,

scope: the root object being accessed by path,

filters: the available filters,

head: the comment node bounding the start of the list,

tail: the comment node bounding the end of the list

}

A block filter can control how context.node is going to be inserted:

by returning a fragment, it replaces the previous fragment

by returning a new node, it replaces context.node (if there was only one

node in the fragment)

by returning false, it prevents context.node from being inserted

and it stops further block filters from being called

by inserting context.node itself somewhere else

Some filters are already available:

invert: insert nodes in reverse order

Tables

It is possible to merge columns, then merge rows, to achieve merging

table data with arbitrary columns.

There's an example of this in

test/repeat.html#should repeat within repeat

Utilities

Domt.each(obj, iter)

obj can be an array, an object, a NodeList, a jQuery object

iter is a function with signature (item, index) called on each element of obj.

Domt.template(name|selector|node)

where name is defined by repeat-name.

returns an instance of a template, upon which the following methods are useful

Domt.createFragment()

creates a fragment from given document or from a new context as far as possible

Domt.import(node, )

import a node in a document or fall back to cloneNode

domt.import()

import current nodes of Domt instance into the given document

template.head, template.tail, template.fragment

the nodes defining a template

template.attach(selector|element|comment)

attach a template to a node.

If it is a comment node, it is used as the new head of the fragment.

If an element is passed, append a comment node to it and work with it.

See test/fragment.html for usages.

template.clone(selector|element|comment)

return a new template with cloned head, fragment and tail

See test/fragment.html for usage.

template.merge(...)

alias of Domt(template).merge(...)

Access to templates is usefull when they are defined in a place and merged

in another place.

It is possible to merge them outside the DOM, in a DocumentFragment like

var inst = Domt.template('myfrag').clone().merge(data);

document.body.appendChild(inst.nodes);

or attach them like

Domt.template('myfrag').clone('#thatid').merge(data)

Best practices

Use bind-data-*

Avoid

and use
instead - avoiding the risk

of having $(div).data('test') returning "[test|json]", in case

the data wasn't merged.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值