Documentation
For an overview of using dna.js, see the Bookstore Example.
For richer examples, view the HTML source of the project specification runner.
Terminology and System Requirements
Terminology
- Template:
-
An HTML element whose clones will be injected with data from JavaScript
objects. The element has the
dna-template
class.
- Data Field:
-
Data fields identify where in the template data is to be injected during the cloning
process. The name of the data field (object property) is surrounded by a pair of
double tildes (example:
~~author~~
). Mustache notation with curly braces ({{
and}}
) is also supported.
- Clone:
- A DOM element that is made by copying a template. The template name is added as each clone's class name.
System Requirements
- Web Browsers:
- dna.js supports Chrome, Edge, Internet Exploder, Firefox, Opera, and Safari.
- Versions:
- dna.js requires jQuery 1.9 or above, and it has the same system requirements and browser support as the version of jQuery you use.
Setup JS and CSS Assets
Get started by including the JavaScript and CSS files into your project.
CDN
The jsDelivr CDN makes it easy to get started.
<link rel=stylesheet href=https://cdn.jsdelivr.net/dna.js/1.2/dna.css>
<script src=https://cdn.jsdelivr.net/jquery/3.1/jquery.min.js></script>
<script src=https://cdn.jsdelivr.net/dna.js/1.2/dna.min.js></script>
Alternatively, you can use one of the methods below to download the dna.js files into a folder in your project.
Install with npm
npm
can download the assets to your project's node_modules
folder.
$ npm install dna.js
You can also run dna.js headless (no browser) on Node.js with the DOM provided by jsdom. See the sample project: dnajs-node-jsdom-starter
Install with webpack
webpack treats the dna.js library as a module. Use require
statements in
your application to pull in the library's CSS and JavaScript.
Then use dna.registerContext()
to expose your application so its functions can be used as callbacks from web pages.
require('dna.js/dna.css');
var $ = require('jquery');
var dna = require('dna.js')(window, $);
var myApp = {
doSomething: function() { ... }
};
dna.registerContext('myApp', myApp);
Check out the sample webpack projects dnajs-webpack-starter and dnajs-webpack-to-do-app.
Install with Bower
Bower
can download the assets to your project's bower_components
folder.
$ bower install dna.js
Rails Assets
Rails projects can download dna.js directly from the Rails Assets repository.
source "https://rails-assets.org" do
gem "rails-assets-dna.js"
end
@import "dna.js";
//= require jquery2
//= require dna.js
HTML — Classes, Fields, Attributes, and Properties
Template
Use the class dna-template
to indicate a template. The id
attribute of the element is the template name.
<div id=book class=dna-template>
Data Field
Enclose data field names in double tildes. Each field takes up the entire text part of the HTML node.
<span>~~title~~</span>
The field name refers to the property of the data object. If the data
is a simple literal (string, number, or boolean) rather than an object,
use the special name [value]
for the field.
<span>~~[value]~~</span>
Element Attribute
Attributes are set using the same notation as fields.
<div id=~~code~~>
Note: See Element Attribute (advanced) for alternate ways to set the value of an attribute.
Element Property
Properties are enabled or disabled based on the value of the specified data field.
<input type=checkbox data-prop-checked=~~isGift~~> Gift Wrap<br>
<input type=checkbox data-prop-checked=~~isExpress~~> Overnight
Valid data attributes for setting a property are data-prop-checked
,
data-prop-selected
, and data-prop-deleted
.
Select Option
The current selected option
of a select
drop-down is set based on
the value of the specified data field matching the value
attribute of the
option
element.
<select data-option=~~category~~>
<option selected disabled>Select category</option>
<option value=art>Arts and humanities</option>
<option value=sci>Science</option>
<option value=med>Medical</option>
</select>
The data-change
attribute can be used in combination
with the data-option
attribute to fire a callback that gets the current data
model whenever the user selects a new drop-down option.
Class
A class can be set just like other attributes.
<div class=~~type~~>
However, the regular attribute technique above wipes out existing classes on the element, so
it is generally better to the use the data-class
attribute to add a class.
<div data-class=~~type~~>
If there are two or three names (comma separated), the first name is still the name of the data field but the data field is evaluated for "real" truth.
<div data-class=~~onSale,red-highlight,no-highlight~~>
For true, the second name is added to the element as a class name while the class for third name, if provided, is removed. For false, the third name, if provided, is added to the element as a class name while the class for second name is removed.
Name | Use | Description |
---|---|---|
1st | Data field name | Data for class name or truthiness |
2nd | Class name (for true) | Class added to element for true |
3rd | Class name (for false) | Class added to element for false |
Continuing with the example of the onSale
data field, the code below would
result in the first book containing an element with the class red-highlight
and
the second book containing an element with the class no-highlight
.
<div id=book class=dna-template>
<span data-class=~~onSale,red-highlight,no-highlight~~>
~~title~~
</span>
</div>
<script>
var books = [
{ title: 'The DOM', onSale: true },
{ title: 'Styling CSS', onSale: false }
];
dna.clone('book', books);
</script>
<div class=book>
<span class=red-highlight>The DOM</span>
</div>
<div class=book>
<span class=no-highlight>Styling CSS</span>
</div>
Ensemble
Below is an example of a completed template.
<div id=book class=dna-template>
<div id=~~code~~>
Title: <span>~~title~~</span>
</div>
<div data-class=~~type~~>
Author: <span>~~author~~</span>
</div>
</div>
Element Attribute (advanced)
In addition to using regular templating to set attributes, you can
prefix data-attr-
to the attribute name.
src
attribute
<img src=# data-attr-src=~~url~~ alt=avatar>
The prefixing technique enables setting the src
attribute for an
img
element without causing the browser to fetch the image until the
src
attribute is actually set (adding src=#
ensures the HTML is
still valid according to the W3 HTML Validator).
This technique can also be used to set the id
when the regular technique would
result in duplicate IDs.
Similarly, data-attr-type
can be used a as a warning free way set HTML5 values
for the type
attribute on input
tags.
date
and color
fields
Due date: <input data-attr-type=date value=~~dueDate~~><br>
Cover color: <input data-attr-type=color value=~~coverColor~~>
Due date: <input type=date value=2017-12-31><br>
Cover color: <input type=color value=#DAA520>
Setting HTML5 type values with the data-attr-type
attribute avoids browser
warnings such as:
-
The specified value "~~dueDate~~" does not conform to the required format, "yyyy-MM-dd".
-
The specified value "~~coverColor~~" does not conform to the required format. The format is "#rrggbb" where rr, gg, bb are two-digit hexadecimal numbers.
JavaScript API
API Overview
- dna.clone(name, data, options)
- dna.cloneSubTemplate(holderClone, arrayField, data, options)
- dna.rest.get(name, url, options)
- dna.getModel(nameOrClone)
- dna.empty(name, options)
- dna.refresh(clone, options)
- dna.refreshAll(name)
- dna.destroy(clone, options)
- dna.getClone(elem, options)
- dna.getClones(name)
- dna.up(elemOrEventOrIndex)
- dna.down(elemOrEventOrIndex)
- dna.bye(elemOrEventOrIndex)
- dna.registerInitializer(callback, options)
- dna.clearInitializers()
- dna.registerContext(contextName, contextObjectOrFunction)
- dna.info()
dna.clone(name, data [, options])
Generates a copy of the template and populates the fields, attributes, and classes from the supplied data.
-
@@include('website/templates/params/name.html')
@@include('website/templates/params/data.html')
-
-
@@include('website/templates/options/fade.html')
@@include('website/templates/options/top.html')
@@include('website/templates/options/html.html')
@@include('website/templates/options/empty.html')
@@include('website/templates/options/holder.html')
@@include('website/templates/options/callback.html')
dna.clone('book', { title: 'The DOM', author: 'Bo' });
dna.cloneSubTemplate(holderClone, arrayField, data[, options])
Clones a sub-template to append onto an array loop.
-
@@include('website/templates/params/holder-clone.html')
@@include('website/templates/params/array-field.html')
@@include('website/templates/params/data.html')
-
-
@@include('website/templates/options/fade.html')
var book = { title: 'Interwebz', chapters: ['ARPANET', 'TCP/IP'] };
var clone = dna.clone('book', book);
dna.cloneSubTemplate(clone, 'chapters', 'CERN'); //append chapter
See the Sub-templates (array loops) section for details.
dna.createTemplate(name, html, holder)
Generates a template from an HTML string.
-
@@include('website/templates/params/name.html')
@@include('website/templates/params/html.html')
@@include('website/templates/params/holder.html')
var books = [
{ title: 'The DOM' },
{ title: 'Go JavaScript!' },
{ title: 'Styling CSS3' }
];
var html = '<li>~~title~~</li>';
dna.createTemplate('book', html, $('ol#book-list'));
dna.clone('book', books);
<ol id=book-list>
<li class=book>The Dom</li>
<li class=book>Go JavaScript!</li>
<li class=book>Styling CSS3</li>
</ol>
dna.rest.get(name, url [, options])
Makes a GET request to the url and then generates a copy of the template and populates the fields, attributes, and classes from the JSON response.
Note: This feature is currently experimental with very limited functionality.
-
@@include('website/templates/params/name.html')
@@include('website/templates/params/url.html')
-
-
@@include('website/templates/options/fade.html')
@@include('website/templates/options/top.html')
@@include('website/templates/options/html.html')
@@include('website/templates/options/empty.html')
@@include('website/templates/options/holder.html')
@@include('website/templates/options/callback.html')
dna.rest.get('book', 'http://dnajs.org/rest/book/3');
The dna.rest.get()
method will not create a clone if the REST
call returns an error
field equal to true
.
For example:
{ "error": true, "message": "Resource not found" }
dna.getModel(clone)
Returns the underlying data of the clone.
-
@@include('website/templates/params/clone.html')
-
-
@@include('website/templates/options/main.html')
var book = { title: 'The DOM', author: 'Bo' };
var clone = dna.clone('book', book);
dna.getModel(clone).title = 'The DOM II';
dna.refresh(clone); //updates ui (DOM element) with new title
dna.empty(name[, options])
Deletes all clones generated from the template.
-
@@include('website/templates/params/name.html')
-
-
@@include('website/templates/options/fade.html')
dna.empty('book', { fade: true });
The dna.empty()
method does not delete sub-clones
generated from nested templates.
dna.refresh(clone[, options])
Updates an existing clone to reflect changes to the data model.
-
@@include('website/templates/params/clone.html')
-
-
@@include('website/templates/options/data.html')
@@include('website/templates/options/main.html')
@@include('website/templates/options/html.html')
var book = { title: 'The DOM', author: 'Bo' };
var clone = dna.clone('book', book);
dna.getModel(clone).title = 'The DOM II';
dna.refresh(clone); //updates ui (DOM element) with new title
dna.refreshAll(name)
Updates all the clones of the specified template.
-
@@include('website/templates/params/name.html')
var clone1 = dna.clone('book', { title: 'The DOM' });
var clone2 = dna.clone('book', { title: 'Styling CSS' });
dna.getModel(clone1).title = 'The DOM II'; //modified title
dna.getModel(clone2).title = 'Styling CSS3'; //modified title
dna.refreshAll('book'); //updates ui with new titles
dna.destroy(clone[, options])
Removes an existing clone from the DOM.
-
@@include('website/templates/params/clone.html')
-
-
@@include('website/templates/options/fade.html')
@@include('website/templates/options/main.html')
var clone = dna.clone('book', book);
dna.destroy(clone, { fade: true });
Note:
If the only option being set is fade
to true
, it is simpler to user
the dna.bye()
convenience method.
dna.getClone(elem[, options])
Returns the clone (or sub-clone) for the specified element.
-
@@include('website/templates/params/elem.html')
-
-
@@include('website/templates/options/main.html')
<div id=book class=dna-template>
<p>~~title~~</p>
<button data-click=setFavorite>I Like</button>
</div>
<script>
function setFavorite(buttonElem) {
dna.getClone(buttonElem).addClass('favorite');
}
dna.clone('book', { title: 'Go JavaScript!' });
</script>
<div class=book>
<p>Go JavaScript!</p>
<button>I Like</button>
</div>
<div class="book favorite">
<p>Go JavaScript!</p>
<button>I Like</button>
</div>
dna.getClones(name)
Returns an array of all the existing clones for the given template.
-
@@include('website/templates/params/name.html')
var books = dna.getClones('book');
dna.up(elemOrEventOrIndex)
Smoothly moves a clone up one slot effectively swapping its position with the previous clone.
-
@@include('website/templates/params/elem-or-event-or-index.html')
<div id=book class=dna-template>
<p>~~title~~</p>
<button data-click=dna.up>Move Up</button>
<button data-click=dna.down>Move Down</button>
</div>
Users will be able to interactively rearrange the clones by clicking the Move Up and Move Down buttons.
dna.down(elemOrEventOrIndex)
Smoothly moves a clone down one slot effectively swapping its position with the next clone.
-
@@include('website/templates/params/elem-or-event-or-index.html')
See the example for dna.up().
dna.bye(elemOrEventOrIndex)
Performs a sliding fade out effect on the clone and then removes the element. The function can be called: 1) as an event callback, 2) from a jQuery loop, or 3) directly with the clone as the parameter.
-
@@include('website/templates/params/elem-or-event-or-index.html')
<div id=book class=dna-template>
<p>~~title~~</p>
<button data-click=dna.bye>Delete Book</button>
</div>
$('.book.favorite').each(dna.bye); //deletes all favorite books
var clone = dna.clone('book', { title: 'The DOM' });
dna.bye(clone); //deletes 'The DOM'
Note 1: The element identifying the clone can be a sub-element within the clone or the clone itslef.
Note 2: The clone to be removed can be either a main clone or a sub-clone.
dna.registerInitializer(callback[, options])
Adds a callback function to the list of initializers that are run on all DOM elements.
-
@@include('website/templates/params/callback.html')
-
-
@@include('website/templates/options/selector.html')
// Line below replaces: $('.comment-form').validate();
dna.registerInitializer('validate', { selector: '.comment-form' });
See the Initializers section for details.
dna.clearInitializers()
Deletes all initializers.
dna.registerInitializer('bootstrapSwitch', '.switch');
dna.clearInitializers(); //undoes previous line
See the Initializers section for details.
dna.registerContext(contextName, contextObjectOrFunction)
Registers an application object or individual function to enable it to be used for event callbacks. Registration is needed when global namespace is not available to dna.js, such as when using webpack to load dna.js as a module.
- Name of the object or function to register.
- The object or function being registered.
See the webpack section for more information.
dna.info()
Returns status information about templates on the current web page.
dna.info();
The dna.info()
method is intended to be called manually
from the console to see which templates have been detected and
compiled.
Looping
Template Array Loops
Pass in a single object to create one clone. Pass in an array of objects to create multiple clones.
<div id=book class=dna-template>~~title~~</div>
var books = [
{ title: 'The DOM' },
{ title: 'Interwebz' }
];
dna.clone('book', books);
<div class=book>The DOM</div>
<div class=book>Interwebz</div>
Sub-templates (array loops)
Use the data-array
attribute to create a sub-template
for data fields that are arrays. Each element of the array will
clone the sub-template to generate a sub-clone within the main clone.
<div id=book class=dna-template>
Book: <span>~~title~~</span> by
<span>
<span data-array=~~authors~~>~~[value]~~</span>
</span>
<div>
<div data-array=~~chapters~~>
Chapter: <span>~~header~~</span>
</div>
</div>
</div>
var book = {
title: 'Interwebz',
authors: ['Ed', 'Jake', 'Abby'],
chapters: { header: 'ARPANET', header: 'TCP/IP' }
};
dna.clone('book', book);
<div class=book>
Book: <span>Interwebz</span> by
<span>
<span>Ed</span>
<span>Jake</span>
<span>Abby</span>
</span>
<div>
<div>
Chapter: <span>ARPANET</span>
</div>
<div>
Chapter: <span>TCP/IP</span>
</div>
</div>
</div>
Arrays of Primitives (strings, numbers, booleans)
The special field [value]
tells the template to use simple values
from an array of primitives. The special field [count]
is the value's index number in the array plus 1.
<div id=book class=dna-template data-title=~~[value]~~>
<span>~~[count]~~</span>. <span>~~[value]~~</span>
</div>
var books = ['The DOM', 'Interwebz'];
dna.clone('book', books);
<div class=book data-title="The DOM">
<span>1</span>. <span>The DOM</span>
</div>
<div class=book data-title="Interwebz">
<span>2</span>. <span>Interwebz</span>
</div>
Separators
Use the data-separator
and data-last-separator
attributes to
insert text between clones. For example, this feature can put commas and the word
"and" between authors in a list. The two types of separators can be used together
or individually.
<b>Authors:</b>
<span id=author class=dna-template data-separator=", ">
<span>~~name~~</span>
</span>
<script>
var authors = [{ name: 'Ed' }, { name: 'Bo'}, { name: 'Jan' }];
dna.clone('author', authors);
</script>
Note:
For more advanced control, use the classes dna-separator
and
dna-last-separator
on elements within the template.
<b>Authors:</b>
<span id=author class=dna-template>
<span>~~name~~</span><span class=dna-separator>,</span>
<span class=dna-last-separator>and</span>
</span>
Events, Callbacks, and Forms
Data Model Transformer
The FINAL chapter in keeping JavaScript out of HTML is here.
Transformers (data-transform
) are the holy grail of semantic templates, enabling
you to cleanly organize your business logic in JavaScript instead creating hideous HTML.
<div id=book class=dna-template data-transform=enhanceBook>
<span>~~title~~</span>
<img src=star.png alt=star data-truthy=~~displayStar~~>
</div>
function enhanceBook(data) {
data.displayStar = data.rating > 3; //good books get a star
}
var books = [
{ title: 'The DOM', rating: 2 },
{ title: 'Interwebz', rating: 5 }
];
dna.clone('book', books);
<div class=book>
<span>The DOM</span>
</div>
<div class=book>
<span>Interwebz</span>
<img src=star.png alt=star>
</div>
Element Callback
Use the data-callback
attribute to declare a callback function for a
template. Cloning the template triggers the callback for each new clone, and the clone
element is passed into the function.
In the example below, the span
node of each new book clone will be passed into
the blink()
function, causing the title to fade in a couple times as if it's
blinking at the user.
<div id=book class=dna-template>
<span data-callback=blink>~~title~~<span>
</div>
function blink(elem) {
elem.fadeOut().fadeIn().fadeOut().fadeIn();
}
Note: Element callbacks perform a similar function as initializers, but an element callback is specific and an initializer is general. For example, an element callback is appropriate for an animation or initiating a REST call to retrieve additional data for the element while an initializer is appropriate for configuring tooltips that occur in any element.
Click, Change, and Key Events
Event bindings in dna.js are done with data attributes directly on DOM elements that need to trigger callbacks. The value assigned to the attribute is the name of the function to be called when the event occurs. The jQuery element which triggered the event is passed into the callback function as the first parameter, and the event object is passed in as the second parameter.
Element attributes for supported events:data-click
data-change
data-key-up
data-key-down
data-key-press
data-enter-key
<p>Click: <button data-click=showMsg value=1>1</button></p>
<p>Change: <input data-change=showMsg value=2></p>
<p>Up: <input data-key-up=showMsg value=3></p>
<p>Down: <input data-key-down=showMsg value=4></p>
<p>Press: <input data-key-press=showMsg value=5></p>
<p>Submit: <input data-enter-key=showMsg value=6></p>
<p id=message></p>
<script>
function showMsg(elem, event) {
var msg = 'Value "' + elem.val() + '" on ' + event.type;
$('#message').text(msg).stop().hide().fadeIn();
}
</script>
Click:
Change:
Up:
Down:
Press:
Submit:
Note:
For text fields input
and textarea
, use
data-smart-update.
Experiment with click events:
Smart Update
Smart update callbacks are run only when a user changes the actual value of a text
input
, and the calls are throttled to be at least two seconds apart.
The data-smart-update
attribute sets the callback, and the
data-smart-throttle
attribute sets the throttle rate in milliseconds to
override the 2000 millisecond default.
<input data-smart-update=logNow placeholder="Type here">
<div id=log-message class=dna-template>
<code>~~message~~</code>
</div>
<script>
function logNow(elem) {
var message = Date.now() + ': ' + elem.val();
dna.clone('log-message', { message: message });
}
</script>
~~message~~
Experiment with smart update events:
Jump to page (URL)
Similar to the <a>
anchor tag, the data-href
attribute
redirects the browser to the specified URL when the user clicks the element.
<button data-href=../book-finder.html>Book Finder</button>
Add class=external-site
to tell the browser to open the link in a new tab (or
window).
Data Model Updating on Field Updates
The clone's data model is updated when a user edits or changes an input field.
Input Field | Example |
---|---|
text | <input value=~~title~~> |
checkbox | <input type=checkbox data-prop-checked=~~set~~> |
radio button | <input type=radio name=cover data-prop-checked=~~set~~> |
drop-down | <select data-option=~~category~~> |
The example below uses event bindings to call a function that reads the data model with dna.getModel().
<h2>Book Information</h2>
<div id=book class=dna-template>
Title: <input value=~~title~~ data-key-up=bookInfo.update>
ebook: <input type=checkbox data-prop-checked=~~ebook~~
data-change=bookInfo.update>
</div>
<div>Data: <code id=data>[YOUR MOVE]</code></div>
var bookInfo = {
update: function(elem) {
var data = JSON.stringify(dna.getModel(elem));
$('#data').text(data).hide().fadeIn();
},
setup: function() {
var book = { title: 'Styling CSS', ebook: true };
dna.clone('book', book);
}
};
$(bookInfo.setup);
Book Information
[YOUR MOVE]
Note:
While the original data used to clone the template may contain
truthy values, the boolean
values updated in the data
model will be set to actual true
or false
.
Initializers
On a static page where all the HTML is sent in a single HTTP response, any needed element
initialization can be done one time just after page load. For example, the line
$('.tooltip').qtip();
could be used to setup all tooltips. Cloning with
dna.js creates elements and adds them to the DOM after the page is loaded, and those new
elements also need to be initialized.
When you register your initializer functions, dna.js ensures DOM elements are setup even if the elements are added to the DOM via cloning after the page is loaded.
Selector | Example | Initializer |
---|---|---|
Yes | { selector: '.tooltip' } |
Immediately run on selected elements (example: $('.tooltip') ) and then on new clones |
No | n/a | Only run on new clones |
If the registered initializer function is a method on the jQuery element, dna.js will execute
the method on the element and use the params
option for the parameters.
Otherwise, the element is passed into the function as the first parameter with the
params
supplied as additional parameters.
var app = {
setup: function() {
function highlight(elem) {
elem.css({ backgroundColor: 'gold' });
}
// Highlight each new clone
dna.registerInitializer(highlight);
// Setup tooltips for each new clone: $('.tooltip').qtip();
dna.registerInitializer('qtip', { selector: '.tooltip' });
// Style each new clone: $('.gap').css({ padding: '8px' });
var options = { selector: '.gap', params: { padding: '8px' } }
dna.registerInitializer('css', options);
}
};
$(app.setup);
Note 1:
Initializers perform a similar function as element
callbacks (data-callback
), but an initializer is general to all clones and
an element callback is specific to clones of one template. For example, an initializer
is appropriate for configuring tooltips that can occur in any clone.
Note 2:
In the event that your application dynamically adds elements to the DOM without using dna.js,
it may be necessary to explicitly run the initializers by passing the root element of new
elements into: dna.events.runInitializers(root);
Callback Options
Once cloning is completed, the callback
function is
executed and passed the clone element plus the data that was injected
into the clone.
<div id=book class=dna-template>
~~title~~
</div>
function applyBkgnd(elem, data) {
elem.css({ backgroundImage: data.cover });
};
var options = { fade: true, callback: applyBkgnd };
var book = { title: 'Taskmaster', cover: 'url(cover.png)' };
dna.clone('book', book, options);
<div class=book style="background-image: url(cover.png);">
Taskmaster
</div>
See the line
dna.clone('circle', circles, { callback: addStyle });
used to create the dna.js logo.
Note:
It is often cleaner and easier to declare an
element callback (data-callback
) than to
pass in a function with the callback
option.
On Load Functions
Use the data-on-load
attribute to initiate a callback function after the
document is ready. The element with the data-on-load
attribute is passed
into on load function once the HTML is loaded.
<p data-on-load=blink>
This paragraph will blink on page load!
</p>
<script>
function blink(elem) {
elem.fadeOut().fadeIn().fadeOut().fadeIn();
}
</script>
The data-on-load
attribute can be used in conjunction with the
dna.ui.focus
function to auto-focus on an input
element.
Structures and Conditional Logic
Object Dot Notation (nested objects)
Use JavaScript object dot notation in field names to reference data fields of nested objects.
<div id=book class=dna-template>
First Name: <span>~~author.first~~</span>
</div>
var book = {
title: 'The DOM',
author: { first: 'Bo', last: 'Smith' }
};
dna.clone('book', book);
<div class=book>
First Name: <span>Bo</span>
</div>
Template Placeholder
A template placeholder is only shown when its corresponding template is empty (has zero
clones). The data-placeholder
attribute specifies the name of the
template.
<div class=books>
<p id=book class=dna-template>~~title~~</p>
</div>
<div data-placeholder=book>No books</div>
The "No books" message is displayed until at least one book is cloned, and the message will be re-displayed if all the book clones are removed.
Thimblerig (conditional hide and seek logic)
Instead of writing JavaScript to show and hide DOM elements, use an attribute to declare whether the element should be shown or hidden. dna.js supports four conditionals — two for the existence of a field and two for the "real" truth of a field.
Attribute | Value | Logic |
---|---|---|
data-require | Data Field | Show element only if specified field has a value |
data-missing | Data Field | Show element only if specified field has no value |
data-truthy | Data Field | Show element only if specified field means true* |
data-falsey | Data Field | Show element only if specified field means false* |
<div id=book class=dna-template>
<span data-require=~~title~~>~~title~~</span>
<span data-missing=~~title~~>[not found]</span>
</div>
var books = [
{ author: 'Bo', title: 'The DOM' },
{ author: 'Jan' }
];
dna.clone('book', books);
<div class=book>
<span>The DOM</span>
</div>
<div class=book>
<span>[not found]</span>
</div>
Real Truth
dna.js evaluates if a data field is true
or false
based on rules
designed to match the boolean meaning of data as it would be stored in a database or how the
data would be interpreted in a business sense. For example, the string
'FALSE'
is evaluated to the boolean false
when determining its
"real" truth.
Evaluate | Examples |
---|---|
Truthy |
true , 1 , '1' , 't' ,
'T' , 'TRue' , '77' , 77 ,
[5] , {} , 'Colbert'
|
Falsey |
false , 0 , '0' ,
'f' , 'F' , 'faLSE' , '' ,
[] , null , undefined , NaN
|
Transient Fields
Compact and maintainable code can be written by basing conditionals on transient data fields.
<p class=warning data-truthy=~~overdue~~>Invoice past due!</p>
var invoice = { amount: 125.00, due: 'April 3, 2015' },
invoice.overdue = new Date(invoice.due).getTime() < Date.now();
dna.clone('invoice', invoice);
In the above example, the invoice
data object has an amount
field
and a due
field. The past due warning is displayed if the boolean
field overdue
, which is calculated on the fly, is set to true
.
Note: Similar to thimblerigs, separators are used to hide and show a delimiter between clones, such as a comma to separate names of authors.
Nested Templates
For additional flexibility, templates can be nestes and then explicitly
cloned with calls to dna.clone()
. A nested template
belongs to a holder template, and the specific holder clone must be
passed into dna.clone()
when cloning a nested template.
View source for spec runner #07.
In most cases, a simple sub-template array loop is the better solution.
Making Reusable Components
Defining a Component
A reusable component can be created using an on load function
(data-on-load
) to make a REST call that gets data for a template.
[data-component=bookshelf] .my-book {
width: 15em;
background-color: plum;
padding: 5px 15px;
}
<div data-component=bookshelf data-on-load=bookshelf.setup>
<h2>Bookshelf</h2>
<p id=my-book class=dna-template>
<span>Title: <b>~~title~~</b></span><br>
<span>Author: <b>~~author~~</b></span>
</p>
<script>
var bookshelf = {
booksUrl: 'http://dnajs.org/rest/book/list/',
setup: function(elem) {
function handle(data) { dna.clone('my-book', data); }
$.getJSON(bookshelf.booksUrl, handle);
}
};
</script>
</div>
Bookshelf
Title: ~~title~~
Author: ~~author~~
Inserting a Component
Examples...
<g:render template="/components/bookshelf" />
Panels (UI Tabs)
Panels are content elements associated with menu items. When the user clicks a menu item, the corresponding panel is displayed (faded in) and the panel element is passed to the callback. Think of dna.js panels as a minimalistic implementation of jQuery UI tabs stripped of all styling.
Example
The interactive example below displays information for the user selected author.
[data-component=author] .dna-menu {
list-style-type: none;
padding: 0px;
margin-top: 10px;
}
[data-component=author] .menu-item {
display: inline;
background-color: lightskyblue;
border: 2px solid silver;
border-radius: 5px;
padding: 5px 10px;
}
[data-component=author] .menu-item.selected,
[data-component=author] .menu-item:hover {
border-color: dimgray;
}
<div data-component=author>
<h2>Author Information</h2>
Select:
<ul id=author-info class=dna-menu>
<li>Jake</li>
<li>Abby</li>
<li>Ed</li>
</ul>
<div id=author-info-panels class=dna-panels>
<section>
<h5>Name: Jake</h5>
<p>Publisher: O'Reilly</p>
<p>Agent: William and Associates</p>
</section>
<section>
<h5>Name: Abby</h5>
<p>Publisher: Addison-Wesley</p>
<p>Agent: Pro Reps</p>
</section>
<section>
<h5>Name: Ed</h5>
<p>Publisher: ASM International</p>
<p>Agent: Feven</p>
</section>
</div>
</div>
Author Information
Select:Experiment with button navigation panels:
Experiment with drop-down navigation panels:
Template-Driven Panels
The interactive example below uses templates to build the menu and the panels.
<div data-component=book-catalog data-on-load=bookCatalog.setup>
<h2>Book Catalog</h2>
<nav id=book-catalog class=dna-menu>
<button id=book-menu-item class=dna-template>
~~[count]~~
</button>
</nav>
<div id=book-catalog-panels class=dna-panels>
<section id=book-panel class=dna-template
data-transform=bookCatalog.addCover>
<h4>~~title~~</h4>
<img src=# data-attr-src=~~cover~~ alt=cover>
</section>
</div>
<script>
var bookCatalog = {
data: [
{ id: 'RLG21IfQDckC', title: 'Designing Apps' },
{ id: '_xB0PyT9Y24C', title: 'CSS3 Guide' },
{ id: 'PXa2bby0oQ0C', title: 'The Good Parts' },
{ id: '4x6Gj_0UHMsC', title: 'Pro jQuery' }
],
addCover: function(data) {
data.cover = 'https://books.google.com/books?id=' +
data.id + '&printsec=frontcover&img=1&zoom=1';
},
setup: function() {
dna.clone('book-menu-item', bookCatalog.data);
dna.clone('book-panel', bookCatalog.data);
}
};
</script>
</div>
Book Catalog
~~title~~
Panel Routes
Create routes in dna.js by adding a data-hash
attribute to each panel
element.
The value specified by data-hash
becomes the fragment identifier (hash) in the
URL.
Optionally, add a data-callback
attribute on the menu element to run a
function whenever a panel is displayed. The callback function receives the panel
element and fragment identifier (hash) for the currently displayed panel.
<h2>Publishers</h2>
<nav id=publisher-info class=dna-menu data-callback=updatePanel>
<button>Pearson</button>
<button>Shueisha</button>
<button>Wiley</button>
</nav>
<div id=publisher-info-panels class=dna-panels>
<section data-hash=pearson>
<h2>Pearson</h2>
<p>UK</p>
</section>
<section data-hash=shueisha>
<h2>Shueisha</h2>
<p>Japan</p>
</section>
<section data-hash=wiley>
<h2>Wiley</h2>
<p>US</p>
</section>
</div>
function updatePanel(panel, hash) {
var url = location.origin + location.pathname + '#' + hash;
if (!panel.find('a').length)
panel.append($('<a>', { text: url, href: url }));
}
Publishers
Pearson
UK
Shueisha
Japan
Wiley
US
Utilities
The utility functions built into dna.js are available to be called directly in the event that they might be generally useful.
Utility Functions Overview
- dna.array.find(array, code)
- dna.array.last(array)
- dna.array.toMap(array, key)
- dna.browser.getParams()
-
dna.pageToken.get(key, value)
dna.pageToken.put(key, defaultValue)
- dna.ui.deleteElem(elemOrEventOrIndex)
- dna.ui.focus(elem)
-
dna.ui.slideFadeDelete(elem)
dna.ui.slideFadeIn(elem, callback)
dna.ui.slideFadeOut(elem, callback)
dna.ui.slideFadeToggle(elem, callback) - dna.ui.slidingFlasher(elem, callback)
- dna.ui.smoothMove(elem, up)
- dna.ui.toElem(elemOrEventOrIndex, that)
- dna.util.apply(func, params)
- dna.util.realTruth(value)
- dna.util.toCamel(kebabCaseStr)
- dna.util.toKebab(camelCaseStr)
- dna.util.value(data, field)
dna.pageToken.get(key, defaultValue)
dna.pageToken.put(key, value)
A simple key/value store specific to the page (URL path) that is cleared out when the user's browser session ends.
- Identifier to lookup data.
- Data to store.
- Data to return if the page token is not found.
var a = dna.pageToken.get('favorite', 0); //a == 0
dna.pageToken.put('favorite', 7);
var b = dna.pageToken.get('favorite', 0); //b == 7
dna.ui.deleteElem(elemOrEventOrIndex)
A flexible function for removing a jQuery element.
-
@@include('website/templates/params/elem-or-event-or-index.html')
dna.ui.deleteElem($('.box:first')); //removes first box
$('.box:last').fadeOut(dna.ui.deleteElem); //removes last box
dna.ui.focus(elem)
Sets focus on the element.
-
@@include('website/templates/params/elem-simple.html')
<label>
<span>Start typing:</span>
<input data-on-load=dna.ui.focus>
</label>
dna.ui.slideFadeDelete(elem)
dna.ui.slideFadeIn(elem[, callback])
dna.ui.slideFadeOut(elem[, callback])
dna.ui.slideFadeToggle(elem[, callback])
Various functions to apply the smooth slide plus fade effect.
-
@@include('website/templates/params/elem.html')
@@include('website/templates/params/callback.html')
dna.ui.slideFadeDelete($('.box').last()); //removes the last box
dna.ui.slideFadeIn($('.box').first()); //shows the box
dna.ui.slidingFlasher(elem[, callback])
Uses a smooth slide down plus fade in effect on an element if it is hidden or a smooth fade in flash if the element is already visible — intended to display an error message.
-
@@include('website/templates/params/elem.html')
@@include('website/templates/params/callback.html')
var elem = $('#msg').text('Error!');
dna.ui.slidingFlasher(elem); //slide down
...
dna.ui.slidingFlasher(elem); //pulse
dna.ui.smoothMove(elem, up)
Uses animation to smoothly slide an element up or down one slot amongst its siblings.
-
@@include('website/templates/params/elem.html')
@@include('website/templates/params/up.html')
<ol>
<li>The DOM</li>
<li id=favorite>Go JavaScript!</li>
<li>Styling CSS3</li>
</ol>
<script>dna.ui.smoothMove($('#favorite'), true);</script>
The book Go JavaScript! will be first on the list after smoothMove()
has
executed.
dna.ui.toElem(elemOrEventOrIndex, that)
A flexible way to get the jQuery element whether it is passed in directly, the target of an event, or comes from the jQuery context.
-
@@include('website/templates/params/elem-or-event-or-index.html')
@@include('website/templates/params/that.html')
function logFadeDone(e) {
console.log('Element faded out:', dna.ui.toElem(e, this));
}
$('.box').last().fadeOut(logFadeDone);
dna.util.apply(func, params)
Calls a function passing in the provided parameters.
-
@@include('website/templates/params/func.html')
@@include('website/templates/params/params.html')
dna.util.apply('app.cart.buy', 7); //app.cart.buy(7);
dna.util.apply(Math.max, [7, 21, -3]); //21;
dna.util.apply('fadeIn', $('h1')); //$('h1').fadeIn();
dna.util.apply('css', [$('h1'), { color: 'red' }]);
dna.util.apply('css', [$('h1'), 'color', 'gold']);
dna.util.realTruth(value)
Return the "real" truth of a value. Whereas JavaScript truthy an falsey is more about existence, the "real" truth is for evaluating boolean data as it is stored in databases or transmitted in REST calls.
-
@@include('website/templates/params/value.html')
dna.util.realTruth('F'); //false
dna.util.toCamel(kebabCaseStr)
Converts a kebab-case string (a code made of lowercase letters and dashes) to camelCase.
-
@@include('website/templates/params/kebab-str.html')
dna.util.toCamel('ready-set-go'); //'readySetGo'
dna.util.toKebab(camelCaseStr)
Converts a camelCase string to kebab-case (a code made of lowercase letters and dashes).
-
@@include('website/templates/params/camel-case-str.html')
dna.util.toKebab('readySetGo'); //'ready-set-go'
dna.util.value(data, field)
Retrieve a value from a field within an object.
-
@@include('website/templates/params/data(object).html')
@@include('website/templates/params/field.html')
var data = { cart: { items: 7 } };
var count = dna.util.value(data, 'cart.items'); //7
Index
List of Classes
-
dna-menu
: Mark element as container for panel menu items -
dna-panels
: Mark element as container for panels -
dna-separator
: Mark element as a separator for multiple clones -
dna-separator-last
: Mark element as the last separator -
dna-template
: Mark an element as a template
List of Data Attributes
-
data-array
: Mark an element as a sub-template array loop -
data-attr-{NAME}
: Set value of an element attribute data-callback
: Pass element into function when clone is created-
data-change
: Callback to run on change event -
data-class
: Add a class to the element -
data-click
: Callback to run on click event -
data-enter-key
: Callback to run on enter key event -
data-falsey
: Show element only if field evaluates to false -
data-hash
: Set the fragment ID (hash) for a panel's URL -
data-href
: Jumps to the URL (like an<a>
link) -
data-key-down
: Callback to run on key down event -
data-key-press
: Callback to run on key press event -
data-key-up
: Callback to run on key up event -
data-missing
: Show element only if field us not defined -
data-on-load
: Pass element into function after HTML is loaded -
data-option
: Set option in a select drop-down -
data-placeholder
: Show element only if named template is empty -
data-prop-checked
: Enable or disable "checked" property -
data-prop-disabled
: Enable or disable "disabled" property -
data-prop-selected
: Enable or disable "selected" property -
data-require
: Show element only if field exists -
data-smart-throttle
: Delay (milliseconds) between smart update callbacks -
data-smart-update
: Throttled callback to run on change of an input value -
data-transform
: Callback to enhance data immediately before cloning -
data-truthy
: Show element only if field evaluates to true
List of jQuery Plugin Functions
-
.dna('bye')
: Slide fade out then remove .dna('clone-sub-template', name, data[, options])
: Clones a sub-template-
.dna('destroy'[, options])
: Remove -
.dna('down')
: Smoothly move down one slot -
.dna('refresh'[, options])
: Update to reflect changes to the data model -
.dna('up')
: Smoothly move up one slot
List of jsFiddle Examples
Example | Link |
---|---|
Add a Book | jsfiddle.net/@@jsFiddle.addABook |
Book Finder | jsfiddle.net/@@jsFiddle.bookFinder |
Click Events | jsfiddle.net/@@jsFiddle.dataClick |
Live Model | jsfiddle.net/@@jsFiddle.liveModel |
Panels Click | jsfiddle.net/@@jsFiddle.panelsClick |
Panels Drop-down | jsfiddle.net/@@jsFiddle.panelsDropDown |
Smart Updates | jsfiddle.net/@@jsFiddle.smartUpdates |
To Do | jsfiddle.net/@@jsFiddle.toDo |
Questions and comments
Tweet your question or comment with #dnajs or post below.