domv
Create views as components using DOM. Run the same code on the browser and on the server.
- Introduction
- For servers
- For browsers
- Tutorial
- Creating a Component
- Subclassing
- HtmlDocument
- Modules
- API Reference
Introduction
This is a library that helps you create your html documents using plain javascript, instead of using a template language. No need to learn yet another language with new syntax. Using domv you can easily break up your page design into smaller components and reuse them in other projects.
In most templating languages reusability is hard because once the html is generated, you can no longer modify it easily. In domv, each component you create or use is a proper OOP class which can be subclassed or modified by calling methods or using setters. After you have set up all your components you can serialize them to html markup.
It should also help reduce coupling in your application. This is because the Components are more clearly separated from each other, there is less dependency between them.
Security is another benefit, user input is not parsed as HTML unless you explicitly want it to be.
This library has unit tests with 100% branch coverage. You can also run this test suite in the various browsers.
For servers
If you would like to use domv on your server using node.js or io.js you need to install domv for your project first:
npm install domv inherits --save
You will then need to obtain a DOM Document
, domv has a convenience function to create this for you using jsdom:
var domv = ;var document = domv;
For browsers
If you would like to use domv in a web browser, you can use one of two methods.
If you are already using browserify for your project, you can simply require domv:
var domv = ;
Or you can generate an UMD bundle:
npm install domvcd node_modules/domvnpm run bundle
This will generate a javascript file for you in build/domv.js
, here's an example on using it:
Tutorial
Creating a Component
Now that you have obtained a DOM Document
and have access to the domv module, you can create new Components. When using the domv API you do not have to worry about which browser or server your code is running in.
The most important part of domv is the Component
class. This example shows how you can create one:
var domv = ;var image = domv;console;// <img src="foo.jpg" height="100" width="200">
Or you can use a convenient shorthand:
var domv = ;var div = domv;var img = domv;var component = ;console;// <div class="columns"><div class="left">This is the left column!</div>// <div class="right">This is the right column...// <img src="foo.jpg" height="100" width="200"></div></div>
Each Component
actually wraps a DOM Node
. This is useful to overcome differences between browsers and jsdom, however the real strength is that it lets you create subclasses of Component
(more on that later). Here is an example on wrapping:
var body = domv;console; // trueconsole; // true
Note that domv.wrap
will always create a new Component
(this is by design):
var body = domv;var body2 = domv;console; // trueconsole; // true
Subclassing
Subclassing Component
is what makes domv useful. A Component
wraps a DOM Node
and by doing so it gives you a stable API to modify the DOM with. When browsers introduce new features, your old Components will still use the same stable API without any clashes in attributes or method names. A lot of DOM methods are proxied with some extra flavoring (such asappendChild
, childNodes
, etc).
Here is an example:
// AuthorInfo.js'use strict';var domv = ; // the constructor for AuthorInfo{ // call the constructor of our super class domvComponent; if this var img = this; var h2 = this; var a = this; var p = this; var text = this; this; this; this; else // wrapping constructor // assertHasClass and assertSelector are used to throw an // Error in case invalid content is given to this constructor this; thisavatar = this; thisname = this; thisnameLink = thisname; thisintroduction = this; // Add a DOM event listener this;} moduleexports = AuthorInfo; // "inherits" module provides prototype chainingAuthorInfo domvComponent; Object; Object; AuthorInfoprototype{ this;};
(If you are using IntelliJ or WebStorm, there is a File Template you can import)
The constructor of a Component
subclass must always accept a node
argument as its first argument (a DOM Node
). If this argument is a Document
the Component must create new DOM elements and attributes.
As a rule of thumb, the outer element of a Component always has a class attribute that contains the name of the Component (beginning with a capital letter). Any other class name begins with a lowercase letter. This is import because it lets you easily debug where generated HTML came from, and it is also important when making sure your CSS rules only match your own Component.
var domv = ;var AuthorInfo = ;var body = domv; var author = id: 500 avatarUrl: 'someone.jpg' displayName: 'Someone' introduction: 'Hi! I am someone!!!'; var authorComponent = document author;body;
This results in the following HTML:
<div class="AuthorInfo" data-id="500"> <img src="someone.jpg" class="avatar"> <h2 class="name"> <a href="/author/500">Someone</a> </h2> <p class="introduction">about me: Hi! I am someone!!!</p></div>
If the node
argument is a different kind of Node
, usually an Element
, the Component must wrap existing content:
var domv = ;var AuthorInfo = ;var body = domv; var element = body;var authorComponent = element;authorComponentdisplayName = 'I just renamed myself!';
The wrapping constructor is especially useful if you would like to modify the DOM in the browser after having received HTML from the server.
Outer and Inner nodes
Each Component
has an "outer" node and an "inner" node. You can access these nodes by using the outerNode
and innerNode
attributes. For most components innerNode
and outerNode
refer to the same Node
. But you can set them to something different anytime (usually in the constructor).
The outerNode
is used whenever you make a change to DOM attributes. Also, if a child Component is added to a parent Component, the outerNode
of the child is used.
The innerNode
is used to add any children and is also used for getters such as childNodes
, firstChild
, etc.
var domv = ;var document = domv;var foo = document;var fooInner = document;foo;fooinnerNode = fooInner;foo;var bar = document;foo;console;
Result:
I am a span
HtmlDocument
HtmlDocument
is a Component
subclass which lets you create or wrap a html document. This includes the html, head, title and body elements, but it also provides a number of convenient methods to easily add style, script elements and JSON data. Unlike normal Components the node
argument in HtmlDocument is optional, if not given, a new Document
is constructed for you.
var domv = ;var author = id: 500 avatarUrl: 'someone.jpg' displayName: 'Someone' introduction: 'Hi! I am someone!!!'; var html = ;htmltitle = 'This is my page title';htmlbaseURI = 'https://example.com/foo';html;html;html;html;html;console;
This gives you:
This is my page title This paragraph is added to the body Someone about me: Hi! I am someone!!!
And this is an example with wrapping:
var domv = ;var html = documentdocumentElement;htmltitle = 'A new title!';var session = html;console;var authorInfo = html;console;
Modules
Because this is all just plain javascript, it is easy to publish your Component
as a node module on npm. This works especially well when you use browserify.
Most Components also need some CSS to function properly. For this purpose stylerefs is a good solution. It lets you list the CSS or LESS files your Component needs in the source code using a require call:
'use strict';var domv = ; './AuthorInfo.less' 'optional' 'filter' 'keywords'; // the constructor for AuthorInfo{ // call the constructor of our super class domvComponent; if this ... SNIP ...
This works a lot like browserify. You simply point the stylerefs
command line tool to your code and it will recursively analyze all the require()
calls in your app in order to find the CSS / LESS files that you need. You can use the output of this tool to generate a single CSS bundle file. (instead of the command line tool you can also use grunt. Look at the stylerefs documentation for more details.
API Reference
You can find a description of the API in the file api.md