Documentation
For an overview of using dna-engine, see the Bookstore Example.
For richer examples, view the HTML source of the project visual specification runner.
dna-engine v is powering the interactive examples on this page.
Terminology
Terminology
- Template:
-
An HTML element whose clones are 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.
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/npm/dna-engine@3.0/dist/dna-engine.css>
<script src=https://cdn.jsdelivr.net/npm/dna-engine@3.0/dist/dna-engine.min.js></script>
Alternatively, you can use one of the methods below to download the dna-engine 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-engine
...
"dependencies": {
"dna-engine": "~3.0"
}
...
import { dna } from 'dna-engine';
You can also run dna-engine headless (no browser) on Node.js with the DOM provided by jsdom. See the sample project: node-jsdom-starter
Install with webpack
webpack treats the dna-engine library as a module.
Use import
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.
// Imports
import 'dna-engine/dist/dna-engine.css';
import 'dna-engine';
// Application
const myApp = {
doSomething() {
// Business logic goes here
},
setup() {
// Initiation code goes here
},
};
// Initialization
dna.registerContext('myApp', myApp); //make application visible to dna-engine
dna.dom.onReady(myApp.setup); //call function myApp.setup() once DOM is ready
Check out the sample webpack project: webpack-to-do-app.
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.
<p id=book class=dna-template>
Best practice is to wrap each template in a container element.
<h1>List of Books</h1>
<section class=books>
<div id=book class=dna-template>
<h2>~~title~~</h2>
</div>
</section>
Wrapping templates improves performance and is more semantic because multiple clones (plural) are closely related and belong in a group (singular).
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.
<label>
<input type=checkbox data-prop-checked=~~isGift~~>Gift Wrap
</label>
<label>
<input type=checkbox data-prop-checked=~~isExpress~~>Overnight
</label>
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 disabled selected>Select category</option>
<option value=art>Arts and humanities</option>
<option value=sci>Science</option>
<option value=med>Medical</option>
</select>
The data-on-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.
To make the drop-down list data-driven, the options can be defined by an array in the data object.
{
category: 'sci',
categories: [
{ code: 'art', label: 'Arts and humanities' },
{ code: 'sci', label: 'Science' },
{ code: 'med', label: 'Medical' }
]
}
<select data-option=~~category~~>
<option disabled selected>Select category</option>
<option data-array=~~categories~~ value=~~code~~>~~label~~</option>
</select>
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~~>
Alternatively, brackets can be used to indicate class names selected by a boolean data field (the data field is evaluated for "real" truth).
<div data-class=~~onSale[highlight,no-highlight]~~>
The first class name is added to the element if the data field evaluates to true. If a second class name is provided, it is added to the element when the field is false.
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 highlight
and
the second book containing an element with the class no-highlight
.
<div id=book class=dna-template>
<h2 data-class=~~onSale,highlight,no-highlight~~>~~title~~</h2>
</div>
const books = [
{ title: 'The DOM', onSale: true },
{ title: 'Styling CSS', onSale: false }
];
dna.clone('book', books);
<div class=book>
<h2 class=highlight>The DOM</h2>
</div>
<div class=book>
<h2 class=no-highlight>Styling CSS</h2>
</div>
Ensemble
Below is an example of a completed template.
<div id=book class=dna-template>
<h2 id=~~code~~>~~title~~</h2>
<div data-class=~~type~~>
Author: <cite>~~author~~</cite>
</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.
Formatters
Formatters Overview
A formatter converts a field of the data model so it is rendered in the desired format.
Note: Formatters work on individual model fields while transformers work on the whole data model object. A transformer can operate on multiple fields, but it's generally simpler and cleaner to use a formatter.
Currency Format
Set the value of the data-format-currency
attribute to an ISO 4217 currency
code, such as jpy
or usd
, to format a currency field to display
monetary values like "¥3,000" and "$4.95".
For data stored as a minor unit currency, use data-format-currency100
instead.
Best practice is to store to monetary values as integers rather than floats.
For example, a price of $29.99 should generally be stored as the integer
value 2999
not as a float.
jpy
and eur
currencies
<div id=book class=dna-template>
<p>Japan: <span data-format-currency=jpy>~~price~~</span></p>
<p>Greece: <span data-format-currency100=eur>~~price~~</span></p>
</div>
<div class=book>
<p>Japan: <span>¥2,999</span></p>
<p>Greece: <span>€29.99</span></p>
</div>
List of ISO 4217 currency codes:
https://en.wikipedia.org/wiki/ISO_4217#Active_codes
Code | Minor Unit | Template HTML | Example Value | Example Display |
---|---|---|---|---|
gbp |
data-format-currency=gbp |
1234.5678 |
£1,234.57 | |
krw |
data-format-currency=krw |
1234.5678 |
₩1,235 | |
eur |
data-format-currency=eur |
2999 |
€2,999.00 | |
eur |
data-format-currency100=eur |
2999 |
€29.99 | |
usd |
data-format-currency=usd |
2999 |
$2,999.00 | |
usd |
data-format-currency100=usd |
2999 |
$29.99 |
Date Format
Use the data-format-date
attribute to format the rendering of a date field.
The date field is a number
representing the milliseconds elapsed since
the UNIX epoch (example: 1904112000000
) or an ISO 8602 timestamp string
(example: '2030-05-04T08:00:00.000Z'
).
general
and utc
dates
<div id=event class=dna-template>
<p>Date: <span data-format-date=general>~~timestamp~~</span></p>
<p>UTC: <span data-format-date=utc>~~timestamp~~</span></p>
</div>
<div class=event>
<p>Date: <span>2030-05-04 1:00am Sat</span></p>
<p>UTC: <span>Sat, 04 May 2030 08:00:00 GMT</span></p>
</div>
There are 17 format codes for the data-format-date
attribute.
Format Code | Example Output | Display Time Zone |
---|---|---|
date | Sat May 04 2030 | local |
general | 2030-05-04 1:00am Sat | local |
general-date | 2030-05-04 | local |
general-day | Sat | local |
general-long | 2030-05-04 1:00am Sat (PDT) | local |
general-time | 1:00am | local |
iso | 2030-05-04T08:00:00.000Z | UTC |
locale | 5/4/2030, 1:00:00 AM | local |
locale-date | 5/4/2030 | local |
locale-time | 1:00:00 AM | local |
simple-date | May 4, 2030 | local |
string | Sat May 04 2030 01:00:00 GMT-0700 (PDT) | local |
time | 01:00:00 GMT-0700 (PDT) | local |
timestamp | 2030-05-04+08:00:00 | UTC |
timestamp-msec | 2030-05-04+08:00:00.000 | UTC |
time-zone | PDT | local |
time-zone-long | Pacific Daylight Time | local |
utc | Sat, 04 May 2030 08:00:00 GMT | UTC |
Number Format
Use the data-format-number
attribute to format numbers.
Set the value to #
, #.#
, #.##
, #.###
,
etc. to configure the fixed-point notation display.
<div id=book class=dna-template>
<p>Copies Sold: <span data-format-number=#>~~copiesSold~~</span></p>
<p>Rating: <span data-format-number=#.#>~~rating~~</span></p>
</div>
<div class=book>
<p>Copies Sold: <span>1,327,842</span></p>
<p>Rating: <span>4.0</span></p>
</div>
Template HTML | Example Value | Example Display |
---|---|---|
data-format-number=#.#### |
-7 |
-7.0000 |
data-format-number=# |
1234.5678 |
1,235 |
data-format-number=#.# |
1234.5678 |
1,234.6 |
data-format-number=#.## |
1234.5678 |
1,234.57 |
data-format-number=#.### |
1234.5678 |
1,234.568 |
Percent Format
Use the data-format-percent
attribute to format ratios (generally 0 to 1) as
percentages, such as "93%" or "2.70%".
Set the value to #
, #.#
, #.##
, #.###
,
etc. to configure the fixed-point notation display.
<div id=book class=dna-template>
<p>Score: <span data-format-percent=#>~~score~~</span></p>
<p>Sales: <span data-format-percent=#.#>~~sales.foreign~~</span></p>
</div>
<div class=book>
<p>Score: <span>90%</span></p>
<p>Sales: <span>12.2%</span></p>
</div>
Template HTML | Example Value | Example Display |
---|---|---|
data-format-percent=#.### |
-1 |
-100.000% |
data-format-percent=# |
0.12345678 |
12% |
data-format-percent=#.# |
0.12345678 |
12.3% |
data-format-percent=#.## |
0.12345678 |
12.35% |
Custom Format Function
Use the data-format
attribute to specify a custom formatting function that
takes the value of the field and returns a string to render.
<div id=book class=dna-template>
<p>Title: <span data-format=app.formatTitle>~~title~~</span></p>
<p>Character: <span data-format=String.fromCharCode>~~ascii~~</span></p>
</div>
const app = {
formatTitle(title) {
return `"${title.toUpperCase()}"`;
},
};
const book = { title: 'Styling CSS', ascii: 8721 }; //sum symbol
dna.clone('book', books);
<div class=book>
<p>Title: <span>"STYLING CSS"</span></p>
<p>Character: <span>∑</span></p>
</div>
Note: The optional second parameter passed into the formatter function is the data model object.
JavaScript API (Essential)
API Overview
- dna.clone(name, data, options)
- dna.getClone(elem, options)
- dna.getModel(nameOrClone)
- dna.refresh(clone, options)
- dna.insert(name, data, options)
dna.clone(name, data [, options])
Generates a copy of the template and populates the fields, attributes, and classes from the supplied data.
- Name of the template to clone.
-
Data to be used for populating the template fields when cloning the
template. If
data
is an array, the length of the array is the number of clones to be generated. -
- Smoothly transition clone elements. Default: false
- Prepend new clone element. Default: false
- Number of times to clone the data. Default: 1
- Inject data as HTML rather than the default text. Default: false
- Clear out existing clones first. Default: false
- Container element to move clone into when using nested templates. Default: null
-
Task to mutate (enhance) data before it is used to create a clone.
The form is
(data) => { ... }
, wheredata
is the data object applied to that clone. Default: null -
Task to be run after the operation completes.
The form is
(elem, data) => { ... };
, whereelem
is the DOM object for the clone anddata
is the data object applied to that clone. Default: null
dna.clone('book', { title: 'The DOM', author: 'Bo' });
dna.getClone(elem[, options])
Returns the clone (or sub-clone) for the specified element.
- Any element within the clone or even the clone itself.
-
-
If
true
, ignore sub-clones and only search for the clone generated from the main template. Default: false
-
If
<div id=book class=dna-template>
<p>~~title~~</p>
<button data-on-click=setFavorite>I Like</button>
</div>
<script>
const setFavorite = (buttonElem) => {
dna.getClone(buttonElem).classList.add('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.getModel(clone)
Returns the underlying data of the clone.
-
An element returned from a call to the
dna.clone()
method. -
-
If
true
, ignore sub-clones and only search for the clone generated from the main template. Default: false
-
If
const book = { title: 'The DOM', author: 'Bo' };
const clone = dna.clone('book', book);
const model = dna.getModel(clone);
model.title = 'The DOM II';
dna.refresh(clone); //updates ui (DOM element) with new title
Note:
To get the data for all the clones of a template, call dna.getModels()
with the template name (String).
For example, dna.getModels('book')
will return an array of book objects.
dna.refresh(clone[, options])
Updates an existing clone to reflect changes to the data model.
-
An element returned from a call to the
dna.clone()
method. -
-
New values for the data model.
Only data model fields that are contained in the
data
option are updated. Default: null -
If
true
, ignore sub-clones and only search for the clone generated from the main template. Default: false - Inject data as HTML rather than the default text. Default: false
-
New values for the data model.
Only data model fields that are contained in the
const book = { title: 'The DOM', author: 'Bo' };
const clone = dna.clone('book', book);
const model = dna.getModel(clone);
model.title = 'The DOM II';
dna.refresh(clone); //updates ui (DOM element) with new title
dna.insert(name, data [, options])
For the specified template, updates the first clone if it already exists otherwise creates the first clone.
In cases where only a single clone is needed to provide simple templating for fields that
appear just once on a web page, call
dna.insert()
.
Unlike dna.clone()
, this function will only create a new clone if needed.
- Name of the template to clone.
-
Data to be used for populating the template fields when cloning the
template. If
data
is an array, the length of the array is the number of clones to be generated. -
- Smoothly transition clone elements. Default:
- Inject data as HTML rather than the default text. Default: false
-
Task to mutate (enhance) data before it is used to create a clone.
The form is
(data) => { ... }
, wheredata
is the data object applied to that clone. Default: null -
Task to be run after the operation completes.
The form is
(elem, data) => { ... };
, whereelem
is the DOM object for the clone anddata
is the data object applied to that clone. Default: null
dna.insert('book', { title: 'The DOM', author: 'Bo' });
JavaScript API (Extra/Advanced)
API Overview
- dna.arrayPush(holderClone, arrayField, data, options)
- dna.empty(name, options)
- dna.refreshAll(name)
- dna.recount(name, options)
- dna.destroy(clone, options)
- dna.getClones(name)
- dna.getIndex(elem, options)
- dna.up(elem)
- dna.down(elem)
- dna.bye(elem)
- dna.registerInitializer(callback, options)
- dna.clearInitializers()
- dna.registerContext(contextName, contextObjectOrFunction)
- dna.info()
dna.arrayPush(holderClone, arrayField, data[, options])
Clones a sub-template to append onto an array loop.
- Clone element that contains the sub-template array loop.
- Name of data field for an array loop.
-
Data to be used for populating the template fields when cloning the
template. If
data
is an array, the length of the array is the number of clones to be generated. -
- Smoothly transition clone elements. Default: false
- Prepend new clone element. Default: false
const book = { title: 'Interwebz', chapters: ['ARPANET', 'TCP/IP'] };
const clone = dna.clone('book', book);
const options = { fade: true, top: false };
dna.arrayPush(clone, 'chapters', 'CERN', options); //append chapter
See the Sub-templates (array loops) section for details.
dna.createTemplate(name, html, holder)
Generates a template from an HTML string.
- Name of the template to clone.
-
HTML string representation of a template. The
html
represents a template with exactly one node at its root. Noid
orclass
attributes are required. - Container element to hold clones that are created from the template.
const books = [
{ title: 'The DOM' },
{ title: 'Go JavaScript!' },
{ title: 'Styling CSS3' }
];
const html = '<li>~~title~~</li>';
const holder = document.getElementById('book-list')
dna.createTemplate('book', html, holder);
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.templateExists(name)
Reports if a template is present.
- Name of the template to clone.
if (dna.templateExists('book'))
dna.clone('book', books);
dna.empty(name[, options])
Deletes all clones generated from the template.
- Name of the template to clone.
-
- Smoothly transition clone elements. Default: false
dna.empty('book', { fade: true });
The dna.empty()
method does not delete sub-clones
generated from nested templates.
dna.refreshAll(name)
Updates all the clones of the specified template.
- Name of the template to clone.
-
-
New values for the data model.
Only data model fields that are contained in the
data
option are updated. Default: -
If
true
, ignore sub-clones and only search for the clone generated from the main template. Default: false - Inject data as HTML rather than the default text. Default: false
-
New values for the data model.
Only data model fields that are contained in the
const clone1 = dna.clone('book', { title: 'The DOM' });
const 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.recount(clone[, options])
Renumbers the counters starting from 1 for the clone and its siblings based on DOM order.
When using the special fields [index]
and
[count]
, you may need to renumber the counters
after moving, inserting or deleting clones.
const books = [
{ title: 'The DOM' }, //count: 1
{ title: 'Go JavaScript!' }, //count: 2
{ title: 'Styling CSS3' } //count: 3
];
const clones = dna.clone('book', books);
dna.down(clones.first()); //move "The DOM" to be just after "Go JavaScript!"
dna.recount(clones.eq(1)); //set "Go JavaScript!" count to 1 and "The DOM" to 2
dna.destroy(clone[, options])
Removes an existing clone from the DOM.
-
An element returned from a call to the
dna.clone()
method. -
- Smoothly transition clone elements. Default: false
-
If
true
, ignore sub-clones and only search for the clone generated from the main template. Default: false -
Task to be run after the operation completes.
The form is
(elem, data) => { ... };
, whereelem
is the DOM object for the clone anddata
is the data object applied to that clone. Default: null
const 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.getClones(name)
Returns an array of all the existing clones for the given template.
- Name of the template to clone.
const books = dna.getClones('book');
dna.getIndex(elem[, options])
Returns the index of the clone.
- Any element within the clone or even the clone itself.
-
-
If
true
, ignore sub-clones and only search for the clone generated from the main template. Default: false
-
If
dna.up(elem)
Smoothly moves a clone up one slot effectively swapping its position with the previous clone.
- Any element within the clone or even the clone itself.
<div id=book class=dna-template>
<p>~~title~~</p>
<button data-on-click=dna.up>Move Up</button>
<button data-on-click=dna.down>Move Down</button>
</div>
Users are able to interactively rearrange the clones by clicking the Move Up and Move Down buttons.
dna.down(elem)
Smoothly moves a clone down one slot effectively swapping its position with the next clone.
- Any element within the clone or even the clone itself.
See the example for dna.up().
dna.bye(elem)
Performs a sliding fade out effect on the clone and then removes the element. The function can be called: 1) as an event callback or 2) directly with the clone as the parameter.
- Any element within the clone or even the clone itself.
<div id=book class=dna-template>
<p>~~title~~</p>
<button data-on-click=dna.bye>Delete Book</button>
</div>
const 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 itself.
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.
- String name of a function or an actual function.
-
- CSS selector. Default: null
- A single value to pass into the function or an array of values where each element in the array is a parameter passed into the function.
-
Run the initializer on the web page (
globalThis.document.body
) once the page loads. Default: true
dna.registerInitializer(libX.bubbleHelp.setup);
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-engine, such as when using webpack to load dna-engine 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>
const 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>
const 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 [index]
is the value's index number in the array.
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>
const 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>
Note:
After moving, inserting, or removing clone elements, you can reset the counter for the
special fields [index]
and [count]
by calling
dna.recount()
.
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>
const 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
Essential to building semantic templates and keeping JavaScript out of your HTML is having a
clean way to mutate (enhance) the data model.
Use the data-transform
attribute to declare transformers that implement your
business logic.
The transformer function receives the clone's data model so it can modify the model before the clone element is created.
<div id=book class=dna-template data-transform=enhanceBook>
<span>~~title~~</span>
<img src=star.png alt=star data-true=~~displayStar~~>
</div>
const enhanceBook = (data) => {
data.displayStar = data.rating > 3; //good books get a star
};
const 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>
Note 1:
The transform function can also be passed as an option
when cloning, such as:
dna.clone('book', books, { transform: enhanceBook })
Note 2: Transformers work on data model objects while formatters work on individual model fields. A transformer can operate on multiple fields, but it's generally simpler and cleaner to use a formatter.
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 and its data model are passed into the function.
In the example below, the span
node of each new book clone is 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>
const showHide = (elem) =>
dna.ui.fadeOut(elem).then(dna.ui.fadeIn);
const blink = (elem) =>
window.setInterval(showHide, 2000);
Note 1:
A callback function can also be passed as an
option, such as:
dna.clone('book', books, { callback: blink });
Note 2: 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-engine 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 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-on-click
data-on-hover-in
data-on-hover-out
data-on-change
data-on-input
data-on-key-down
data-on-key-up
data-on-enter-key
data-on-focus-in
data-on-focus-out
<output id=message></output>
<p>Click: <button data-on-click=showMsg value=1>1</button></p>
<p>Hover in: <meter data-on-hover-in=showMsg value=2 max=4></meter> 2</p>
<p>Hover out: <meter data-on-hover-out=showMsg value=3 max=4></meter> 3</p>
<p>Change: <input data-on-change=showMsg value=4></p>
<p>Input: <input data-on-input=showMsg value=5></p>
<p>Key down: <input data-on-key-down=showMsg value=6></p>
<p>Key up: <input data-on-key-up=showMsg value=7></p>
<p>Submit: <input data-on-enter-key=showMsg value=8></p>
<p>Focus in: <input data-on-focus-in=showMsg value=9></p>
<p>Focus out: <input data-on-focus-out=showMsg value=A></p>
<script>
const showMsg = (elem, event) => {
const msg = 'Value "' + elem.value + '" on ' + event.type;
const output = globalThis.document.getElementById('message');
dna.ui.pulse(output, { text: msg });
};
</script>
Click:
Hover in:
Hover out:
Change:
Input:
Key down:
Key up:
Submit:
Focus in:
Focus out:
Note:
For text fields input
and textarea
, use
data-on-smart-update.
Experiment with click events:
See how events can be used with FileReader()
to make a reusable photo upload
component:
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 one second apart.
The data-on-smart-update
attribute sets the callback, and the
data-smart-throttle
attribute sets the throttle rate in milliseconds to
override the 1000 millisecond default.
<input data-on-smart-update=logNow placeholder="Type here">
<div id=log-message class=dna-template>
<code data-format-date=timestamp>~~time~~</code>:
<strong>~~message~~</strong>
</div>
<script>
const logNow = (elem) => {
const data = { time: Date.now(), message: elem.value };
const clone = dna.clone('log-message', data, { fade: true });
clone.firstElementChild.style.color = 'royalblue';
};
</script>
~~time~~
:
~~message~~
Experiment with smart update events:
Smart update events can be used over WebSockets:
github.com/dna-engine/smart-update-websockets

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=../tutorial-files/book-finder.html>Book Finder</button>
Add class=external-site
to tell the browser to open the link in a new tab (or
window).
Add data-target=[WINDOW-NAME]
to tell the browser to open the link in a named 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-on-key-up=bookInfo.update>
ebook: <input type=checkbox data-prop-checked=~~ebook~~
data-on-change=bookInfo.update>
</div>
<div>Data: <code id=data>[YOUR MOVE]</code></div>
const bookInfo = {
update(elem) {
const data = JSON.stringify(dna.getModel(elem));
const dataElem = globalThis.document.getElementById('data');
dataElem.textContent = data;
dna.ui.pulse(dataElem, { duration: null });
},
setup() {
const book = { title: 'Styling CSS', ebook: true };
dna.clone('book', book);
},
};
dna.dom.onReady(bookInfo.setup);
Book Information
ebook:
[YOUR MOVE]
Note:
While the original data used to clone the template may contain
truthy values, the boolean
values updated in the data
model are 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. Cloning with dna-engine 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-engine 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 and then on elements in new clones that match the selector. |
No | n/a | Only run on new clones |
The element is passed into the function as the first parameter with the
params
supplied as additional parameters.
const app = {
setup() {
const highlight = (elem) =>
elem.style.backgroundColor = 'gold';
dna.registerInitializer(highlight); //highlight each new clone
},
};
dna.dom.onDomReady(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-engine, it may be necessary to explicitly run the initializers by passing the root
element of new elements into: dna.events.runInitializers(root);
Transform Option
The transform
option for dna.clone()
specifies a transformer function to mutate (enhance) the data model.
The transformer function receives the clone's data model so it can modify the model before the clone element is created.
<div id=book class=dna-template>
<span>~~title~~</span>
<img src=star.png alt=star data-true=~~displayStar~~>
</div>
const enhanceBook = (data) => {
data.displayStar = data.rating > 3; //good books get a star
};
const books = [
{ title: 'The DOM', rating: 2 },
{ title: 'Interwebz', rating: 5 }
];
dna.clone('book', books, { transform: enhanceBook });
<div class=book>
<span>The DOM</span>
</div>
<div class=book>
<span>Interwebz</span>
<img src=star.png alt=star>
</div>
Note:
A transformer function can also be specified with the
data-transform attribute, such as:
<div id=book class=dna-template data-transform=enhanceBook>
Callback Option
The callback
option for dna.clone()
specifies a callback function.
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>
const applyBkgnd = (elem, data) => {
elem.css({ backgroundImage: data.cover });
};
const options = { fade: true, callback: applyBkgnd };
const 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-engine logo.
Note:
A callback function can also be specified with the
data-callback attribute, such as:
<div id=book class=dna-template data-callback=blink>
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.
The callback function is executed after the function plus any optional dependencies
have been loaded.
Specify dependencies as a comma separated list using the data-wait-for
attribute.
<p data-on-load=doubleBlink data-wait-for=myEventLogger>
This paragraph will blink twice on page load!
</p>
<script>
const doubleBlink = (elem) => {
dna.ui.fadeOut(elem)
.then(dna.ui.fadeIn)
.then(dna.ui.fadeOut)
.then(dna.ui.fadeIn)
.then(() => myEventLogger('Double blink happened'));
};
</script>
Note #1:
The data-on-load
attribute can be used in conjunction with the
dna.ui.focus
function to auto-focus on an input
element.
Note #2: The polling interval to check if the callback function and dependencies are loaded is 300 millisecond.
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>
const 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.
<ol class=books>
<li id=book class=dna-template>~~title~~</li>
</ol>
<p data-placeholder=book>No books</p>
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-engine 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 exists* |
data-missing | Data Field | Show element only if specified field does not exist* |
data-true | Data Field | Show element only if specified field means true** |
data-false | 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>
const 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>
*Field Exists
A field in a data object exists if the field's value is not undefined
or
null
.
**Real Truth
dna-engine 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.
Value | Examples |
---|---|
Truthy |
true , 1 , '1' , 't' ,
'T' , 'TRue' , 'Y' , 'yes' ,
77 , [5] , {} , 'Colbert' ,
Infinity
|
Falsey |
false , 0 , '0' , 'f' ,
'F' , 'faLSE' , 'N' , 'no' ,
'' , [] , null , undefined ,
NaN
|
Transient Fields
Compact and maintainable code can be written by basing conditionals on transient data fields.
<p class=warning data-true=~~overdue~~>Invoice past due!</p>
const 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 nested 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.
Hide a Container Element
Add the class dna-hide
to a container element that should not be visible until
after a descendant template is cloned.
<section data-component=bookshelf class=dna-hide>
<h2>Bookshelf</h2>
<div class=books>
<h3 id=book class=dna-template>~~title~~</h3>
</div>
</section>
The data-component=bookshelf
element is hidden until the book
template is cloned, thus preventing the user from seeing the
<h2>Bookshelf</h2>
heading before there are any books.
Also see: data-placeholder
Making Reusable Components
Defining a Component
A reusable component is named using the data-component
attribute on the
container element.
The component can be initialized with an on load function
(data-on-load
) to make a REST call that gets data for a template.
Events with callbacks, such as a click event
(data-on-click
), pass back the target element, event object, and the component
element.
[data-component=bookshelf] .my-books .my-book {
max-width: 400px;
background-color: plum;
padding: 5px 15px;
margin-bottom: 15px;
}
<div data-component=bookshelf data-on-load=bookshelf.setup>
<h2>Bookshelf</h2>
<section class=my-books>
<div id=my-book class=dna-template>
<h3>~~title~~</h3>
Author: <cite>~~author~~</cite>
</div>
</section>
<button data-on-click=bookshelf.show>Show component name</button>
<script>
const bookshelf = {
booksUrl: 'https://dna-engine.org/api/books/',
show(elem, event, component) {
elem.textContent = component.dataset.component + '!';
},
setup(elem) {
const handle = (data) => dna.clone('my-book', data);
fetchJson.get(bookshelf.booksUrl).then(handle);
},
};
</script>
</div>
Bookshelf
~~title~~
Author: ~~author~~Example Photo Upload Component
See how events can be used with FileReader()
to make a reusable photo upload
component:
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.
Panel basics
Add the dna-menu
class to the container of clickable menu items.
Add the dna-panels
class to the container of panel elements.
<nav class=dna-menu data-callback=myApp.updatePanel>
<span>Menu item #1</span>
<span>Menu item #2</span>
<span>Menu item #3</span>
</nav>
<div class=dna-panels>
<section>Content of panel #1</section>
<section>Content of panel #2</section>
<section>Content of panel #3</section>
</div>
Place the panels container (.dna-panels
) in the DOM immediately after the
menu container (.dna-menu
).
If the menu container and panels container need to be separated in the DOM, link the menu
to the panels by setting their respective data-menu-nav
attributes to the same
name.
The optional data-callback
attribute specifies a function that will be
callled immediately before a panel is displayed.
The function will be passed the panel element for the panel being displayed.
data-menu-nav
<nav class=dna-menu data-menu-nav=author-info>
<span>Menu item #1</span>
<span>Menu item #2</span>
<span>Menu item #3</span>
</nav>
<meter value=7 min=0 max=10></meter>
<div class=dna-panels data-menu-nav=author-info>
<section>Content of panel #1</section>
<section>Content of panel #2</section>
<section>Content of panel #3</section>
</div>
For an example of tabbed panel navigation, check out DataDashboard:
https://data-dashboard.js.org
Example panels
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;
.dna-menu-item {
display: inline;
background-color: lightskyblue;
border: 2px solid silver;
border-radius: 5px;
padding: 5px 10px;
transition: all 400ms;
&.dna-selected, &:hover {
border-color: dimgray;
}
}
}
}
<div data-component=author>
<h2>Author Information</h2>
Select:
<ul class=dna-menu data-menu-nav=author-info>
<li>Jake</li>
<li>Abby</li>
<li>Ed</li>
</ul>
<div class=dna-panels data-menu-nav=author-info>
<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:Name: Jake
Publisher: O'Reilly
Agent: William and Associates
Name: Abby
Publisher: Addison-Wesley
Agent: Pro Reps
Name: Ed
Publisher: ASM International
Agent: Feven
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 class=dna-menu data-menu-nav=book-catalog>
<button id=book-menu-item class=dna-template>
~~[count]~~
</button>
</nav>
<div class=dna-panels data-menu-nav=book-catalog>
<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>
const bookCatalog = {
data: [
{ id: 'RLG21IfQDckC', title: 'Designing Apps' },
{ id: '_xB0PyT9Y24C', title: 'CSS3 Guide' },
{ id: 'PXa2bby0oQ0C', title: 'The Good Parts' },
{ id: '9U5I_tskq9MC', title: 'Eloquent JavaScript' },
{ id: '3TJwDwAAQBAJ', title: 'Mastering SVG' },
],
addCover(data) {
data.cover = 'https://books.google.com/books/content?id=' +
data.id + '&printsec=frontcover&img=1&zoom=1';
},
setup() {
dna.clone('book-menu-item', bookCatalog.data);
dna.clone('book-panel', bookCatalog.data);
},
};
</script>
</div>
Book Catalog
~~title~~
Panel Routes
Create routes in dna-engine 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 class=dna-menu data-menu-nav=publisher-info data-callback=updatePanel>
<button>Pearson</button>
<button>Shueisha</button>
<button>Wiley</button>
</nav>
<div class=dna-panels data-menu-nav=publisher-info>
<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>
const updatePanel = (panel, hash) => {
// Apply new random color to the panel border on each view
const colors = ['aqua', 'gold', 'lime', 'red', 'tan'];
panel.style.borderColor = colors[Date.now() % colors.length];
};
Publishers
Pearson
UK
Shueisha
Japan
Wiley
US
Styling Panels
It's straightforward to style your panel navigation with CSS.
See the CSS for the examples below:
panel-nav.css
Flat tabs
<h2>Book format</h2>
<nav class=dna-menu data-menu-nav=book-format data-style=flat-tabs>
<span>Hardcover</span>
<span>Paperback</span>
<span>eBook</span>
</nav>
<div class=dna-panels data-menu-nav=book-format>
<p>Hardcovers are the nicest.</p>
<p>Paperbacks are the easiest.</p>
<p>EBooks are the future.</p>
</div>
Book format
Hardcovers are the nicest.
Paperbacks are the easiest.
eBooks are the future.
Pillbox tabs
<h2>Book type</h2>
<nav class=dna-menu data-menu-nav=book-type data-style=pillbox-tabs>
<span>Action</span>
<span>Mystery</span>
<span>Science Fiction</span>
</nav>
<div class=dna-panels data-menu-nav=book-type>
<p>Action books</p>
<p>Mystery books</p>
<p>Science fiction books are the best!</p>
</div>
Book type
Action books rock!
Mystery books rule!
Science fiction books are the best!
Example website with pillbox UI tabs:
CLABE Validator
Utilities
The utility functions built into dna-engine 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.fromMap(map, key)
- dna.array.toMap(array, key)
- dna.pageToken
dna.pageToken.get(key, value)
dna.pageToken.put(key, defaultValue)
- dna.ui.focus(elem)
- dna.ui.getAttrs(elem)
- dna.ui.getComponent(elem)
- dna.ui.pulse(elem, options)
- dna.ui.slideFade
dna.ui.slideFadeDelete(elem)
dna.ui.slideFadeIn(elem)
dna.ui.slideFadeOut(elem)
- dna.ui.smoothHeight(callback)
- dna.ui.smoothMove(elem, up)
- dna.util.apply(func, params)
- dna.util.assign(data, field, value)
- dna.util.getFn(funcName)
- dna.util.isObj(value)
- dna.util.printf(formatString, parameters...)
- dna.util.realTruth(value)
- dna.util.toCamel(kebabCaseStr)
- dna.util.toKebab(camelCaseStr)
- dna.util.value(data, field)
dna.array.find(array, value, key)
Returns the index and a reference to the first array element with a key equal to the supplied value. The default key is "code".
- An array of objects.
- An object, array, or primitive of any value.
- The name of a field in an object.
const things = [
{ code: 'a', word: 'Ant' },
{ code: 'b', word: 'Bat' },
];
const x = dna.array.find(things, 'b'); //{ index: 1, item: { code: 'b', word: 'Bat' } }
const y = dna.array.find(things, 'b').index; //1
const z = dna.array.find(things, 'x'); //{ index: -1, item: null }
dna.array.fromMap(object, key)
Converts an object (hash map) into an array of objects. The default key is "code".
- An object literal.
- The name of a field in an object.
const map = {
a: { word: 'Ant' },
b: { word: 'Bat' },
};
const array = dna.array.fromMap(map);
// New array:
// [{ code: 'a', word: 'Ant' }, { code: 'b', word: 'Bat' }]
dna.array.toMap(array, key)
Converts an array of objects into an object (hash map). The default key is "code".
- An array of objects.
- The name of a field in an object.
const words = [
{ code: 'a', word: 'Ant' },
{ code: 'b', word: 'Bat' },
];
const wordMap = dna.array.toMap(words);
// wordMap:
// { a: { code: 'a', word: 'Ant' }, b: { code: 'b', word: 'Bat' } }
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.
const a = dna.pageToken.get('favorite', 0); //a === 0
dna.pageToken.put('favorite', 7);
const b = dna.pageToken.get('favorite', 0); //b === 7
dna.ui.focus(elem)
Sets focus on an element.
- An element.
<label>
<span>Start typing:</span>
<input data-on-load=dna.ui.focus>
</label>
dna.ui.getAttrs(elem)
Returns the attributes of the element in a regular array.
- An element.
dna.ui.getComponent(elem)
Returns the component (container element with a data-component
attribute) to
which the element belongs or null
if the element is not in a component.
- An element.
dna.ui.pulse(elem[, options])
Fades in an element after hiding it to create a single smooth flash effect. If the element is initially hidden, it will slide down.
- Any element within the clone or even the clone itself.
-
- Milliseconds for the action to complete. Default: 7000
- Milliseconds for the fade in animation to run. Default: 600
- Milliseconds for the fade out animation to run. Default: 3000
-
If
true
, ignoredurationOut
. Default: false -
Set the element's text content (
textContent
property) to the given string ornull
to not update the text. Default: null
const msgElem = document.getElementById('msg');
msg.textContent = 'Error!';
dna.ui.pulse(msgElem); //simple flash effect and slow fade out
This UI function is primarily intended to display a status or error message.
dna.ui.slideFadeDelete(elem)
dna.ui.slideFadeIn(elem)
dna.ui.slideFadeOut(elem)
Various functions to apply the smooth slide plus fade effect.
- Any element within the clone or even the clone itself.
- A JavaScript promise for an element.
const boxes = document.getElementsByClass('box');
dna.ui.slideFadeIn(boxes[0]); //shows the 1st box
dna.ui.slideFadeDelete(boxes[1]); //removes the 2nd box
dna.ui.smoothHeight(callback[, options])
Smoothly animates the height of a container element from a beginning height to a final height.
If no container element (elem
) is specified, the <body>
element will be used.
- String name of a function or an actual function.
-
- Element to apply action. Default: <body>
-
Set the CSS
overflow
property to'hidden'
. Default: true - Milliseconds for the action to complete. Default: 400
- A JavaScript promise for an element.
Number of chapters:<br>
<input type=range value=10
data-on-load=makeChapters data-on-change=smooth>
<div id=smooth-container>
<button id=chapter-box class=dna-template>~~[count]~~</button>
</div>
<script>
const makeChapters = (range) => {
const options = { empty: true, clones: +range.value };
dna.clone('chapter-box', {}, options);
};
const smooth = (range) => {
const container = document.getElementById('smooth-container');
const updateUI = () => makeChapters(range);
dna.ui.smoothHeight(updateUI, { container: container, transition: 2000 });
};
</script>
dna.ui.smoothMove(elem, up)
Uses animation to smoothly slide an element up or down one slot amongst its siblings.
- Any element within the clone or even the clone itself.
-
Direction to move element is up (
true
) or down (false
).
<ol>
<li>The DOM</li>
<li id=favorite>Go JavaScript!</li>
<li>Styling CSS3</li>
</ol>
<script>
const elem = document.getElementById('favorite');
dna.ui.smoothMove(elem, true);
</script>
The book Go JavaScript! will be first on the list after smoothMove()
has
executed.
dna.util.apply(func, params)
Calls a function passing in the provided parameters.
- String name of a function or an actual function.
- An array of values to be used are arguments passed into a function.
dna.util.apply('app.cart.buy', [7]); //app.cart.buy(7);
dna.util.apply(Math.max, [7, 21, -3]); //21;
const elem = document.getElementById('user007');
dna.util.apply('dna.ui.smoothMove', [elem, true]); //dna.ui.smoothMove(elem, true);
dna.util.assign(data, field, value)
Sets the field in the data object to the new value and returns the updated data object.
- Object with fields containing data.
- Name of a field within the data object (supports object dot notation for nested objects).
- An object, array, or primitive of any value.
const data = { cart: { items: 7 } };
dna.util.assign(data, 'cart.items', 8);
// New data: { cart: { items: 8 } }
Note: See dna.util.value() for the complementary action of reading a field value from an object.
dna.util.getFn(funcName)
Converts a dot notation name (string) to its callable function.
- Name of a function. Supports dot notation strings.
const buyFn = dna.util.getFn('app.cart.buy');
buyFn(7); //equivalent to executing: app.cart.buy(7);
dna.util.isObj(value)
Determines if something is a real object and not a primitive or array.
- An object, array, or primitive of any value.
return dna.util.isObj(it) ? it : { value: it }; //always returns an object
dna.util.printf(formatString, args...)
Builds a formatted string by replacing the format specifiers with the supplied arguments.
-
String with format specifiers (
'%s'
) indicating where arguments are to be inserted. - Arguments used to replace format specifiers.
dna.util.printf('%s: %s', 'Lives', 3); //'Lives: 3'
dna.util.realTruth(value)
Returns the "real" boolean truth of a value. Whereas JavaScript truthy and falsey are more about existence, the "real" truth is for evaluating boolean data as it is stored in databases or transmitted in REST calls.
- An object, array, or primitive of any value.
dna.util.realTruth('F'); //false
dna.util.toCamel(kebabCaseStr)
Converts a kebab-case string (a code made of lowercase letters and dashes) to camelCase.
- A string made of lowercase letters and dashes.
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).
- A camelCase string.
dna.util.toKebab('readySetGo'); //'ready-set-go'
dna.util.value(data, field)
Returns the value of the field from the data object.
- Object with fields containing data.
- Name of a field within the data object (supports object dot notation for nested objects).
const data = { cart: { items: 7 } };
const count = dna.util.value(data, 'cart.items'); //7
Note: See dna.util.assign() for the complementary action of setting a value in an object.
Index
List of Classes
-
dna-hide
: Hides a container element until a descendant template is cloned -
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-class
: Add a class to the element -
data-component
: Mark a container element as a component -
data-false
: Show element only if field evaluates to false -
data-format-{TYPE}
: Format a value for proper rendering -
data-format
: Custom format function -
data-hash
: Set the fragment ID (hash) for a panel's URL -
data-href
: Jumps to the URL (like an<a>
link) -
data-last-separator
: Insert text, like a comma, between clones -
data-missing
: Show element only if field does not exist -
data-on-change
: Callback to run on change event -
data-on-click
: Callback to run on click event -
data-on-enter-key
: Callback to run on enter key event -
data-on-focus-in
: Callback to run when an element receives focus -
data-on-focus-out
: Callback to run when an element loses focus -
data-on-hover-in
: Callback to run on mouse enter event -
data-on-hover-out
: Callback to run on mouse leave event -
data-on-input
: Callback to run on input event -
data-on-key-down
: Callback to run on key down event -
data-on-key-up
: Callback to run on key up event -
data-on-load
: Pass element into function after HTML is loaded -
data-on-smart-update
: Throttled callback to run on change of an input value -
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-separator
: Insert text between the last two clones -
data-smart-throttle
: Delay (milliseconds) between smart update callbacks -
data-transform
: Callback to enhance data immediately before cloning -
data-true
: Show element only if field evaluates to true -
data-wait-for
: Specify optional dependencies for data-on-load
List of jsFiddle Examples
Example | Link |
---|---|
📚 Add a Book | jsfiddle.net/12nrev6j |
📘 Book Finder | jsfiddle.net/ztsr4qm9 |
🎟️ Click Events | jsfiddle.net/Lkejw4sd |
☯️ Live Model | jsfiddle.net/p5exnfuq |
🗂️ Panels Click | jsfiddle.net/5kguwjaq |
🎛 Panels Drop-down | jsfiddle.net/2cmkzxt0 |
🌊 Photo Upload | jsfiddle.net/f4y5cjon |
⚡️ Smart Updates | jsfiddle.net/pj8vkxrw |
✅ To Do | jsfiddle.net/4jkua81f |
Questions and comments
Tweet your question or comment with #dna-engine or submit an issue on GitHub.
Questions and comments