0.1.0 • Published 4 years ago

dommini v0.1.0

Weekly downloads
2
License
MIT
Repository
github
Last release
4 years ago

dommini

npm

JavaScript package for easy and lightweight creation of DOM trees and HTML files. May be used as a template language (originally created from a module used for a private JSDoc template)

Features:

  • no dependencies
  • minimalistic syntax (see below demo and examples)
const {Document,tags,utils} = require("dommini")
const {link,script,div,span,ol,li,a,p} = tags
const {text,comment,attr,addClass,shortcut} = utils

const customtag = shortcut("customtag")

doc = new Document({title:"dommini demo"})
doc.head.add([
   link().attr({rel:"stylesheet",href:"style.css"}),
   script().attr({type:"text/javascript",src:"script.js"})
])
doc.body.add(function(){
   div(ol(["home", "about", "contact"].map(title => {
      return li(a(title).attr({href:`${title}.html`}))
   }))).id("header").class("h-cls")
   comment("some comment")
   div(function(){
      attr({id:"body",class:"some-class"})
      addClass("another-class")
      p("Lorem ipsum")
      text("dolor sit amet")
   })
   customtag(div(),span())
})
console.log(doc.toString())

Output:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="generator" content="dommini 0.1.0">
    <title>dommini demo</title>
    <link rel="stylesheet" href="style.css">,
    <script type="text/javascript" src="script.js"></script>
  </head>
  <body>
    <div id="header" class="h-cls">
      <ol>
        <li>
          <a href="home.html">home</a>
        </li>
        <li>
          <a href="about.html">about</a>
        </li>
        <li>
          <a href="contact.html">contact</a>
        </li>
      </ol>
    </div>
    <!--some comment-->
    <div id="body" class="some-class another-class">
      <p>Lorem ipsum</p>
      dolor sit amet
    </div>
    <customtag>
      <div></div>
      <span></span>
    </customtag>
  </body>
</html>

See usage section for more details.

Disclaimer: the true dommini output is "one-line" HTML code, please use an external lib to prettify it.

Installation

npm install dommini

Documentation

Usage / documentation

Table of Contents

Hello, world!

const {html,body,h1} = require("dommini").tags
var e = html(body(h1('Hello, world!')))
console.log(e.toString())
<html>
  <body>
    <h1>
      Hello, world!
    </h1>
  </body>
</html>

Elements

Creating

With new keyword

as new Element(tag,...), new CommentNode(comment) or new TextNode(text):

const {Element,TextNode,CommentNode} = require("dommini")
var e = new Element("html", new Element("body",
   new CommentNode("some comment"),
   new Element("h1", "Hello"),
   new TextNode("world!")
))
console.log(e.toString())
<html>
  <body>
    <!--some comment-->
    <h1>Hello</h1>
    world!
  </body>
</html>
Shortcuts

dommini.tags (should) contain shortcuts for all HTML5 tags. dommini.utils defines shortcut for text node and comment node.

const {tags,utils} = require("dommini")
const {html,body,h1} = tags
const {text,comment} = utils
var e = html(body(
   comment("some comment"),
   h1("Hello"),
   text("world!")
))
console.log(e.toString())
<html>
  <body>
    <!--some comment-->
    <h1>Hello</h1>
    world!
  </body>
</html>
Custom elements

Element tag can be any string. dommini.utils.shortcut can also be used to shortcut it.

const {Element,utils} = require("dommini")
const {shortcut} = utils
const [customdiv,customspan] = [shortcut("customdiv"),shortcut("customspan")]
var e = customdiv([
   new Element("customh1"),
   customspan(),
])
console.log(e.toString())
<customdiv>
  <customh1></customh1>
  <customspan></customspan>
</customdiv>

Attributes

Setting

Set attributes

  • with elem.attr(...) method passing an - key-value as two parameters - object of key-value pairs
  • using shortcuts methods (currently for elem.id and elem.class)
  • using elem.addClass to add class, passing - string - strings - array of strings
  • using context and utils.attr and utils.addClass:
const {tags,utils} = require("dommini")
const {div} = tags
const {attr,addClass} = utils
// key-value two parameters
var e = div().attr("k1","val1")
console.log(e.toString())
// key-value object
var e = div().attr({k1:"val1",k2:"val2"})
console.log(e.toString())
// attr shortcuts
var e = div().id("42").class("cls")
console.log(e.toString())
// addClass - one string
var e = div().class("cls").addClass("cls-2")
console.log(e.toString())
// addClass - strings
var e = div().class("cls").addClass("cls-2","cls-3")
console.log(e.toString())
// addClass - array of strings
var e = div().class("cls").addClass(["cls-2","cls-3"])
console.log(e.toString())
// context
var e = div().context(function(){
   addClass("cls")
   attr({k1:"val1",k3:"val3"})
   addClass("cls-2")
})
console.log(e.toString())
<div k1="val1"></div>
<div k1="val1" k2="val2"></div>
<div id="42" class="cls"></div>
<div class="cls cls-2"></div>
<div class="cls cls-2 cls-3"></div>
<div class="cls cls-2 cls-3"></div>
<div k1="val1" k3="val3" class="cls cls-2"></div>
Getting

To get currently set attributes, just call elem.attr() with no argument:

const {div} = require("dommini").tags
var e = div().id("42").class("cls").attr("key","val")
console.log(e.attr())
{ id: '42', key: 'val', class: 'cls' }

Adding children

At creation

Child nodes can be added at creation time:

  • as a single element
  • as multiple elements
  • as an array of elements
  • using context node
const {div,span,h1} = require("dommini").tags
// add one element
var e = div(span())
console.log(e.toString())
// add elements
var e = div(h1(),span())
console.log(e.toString())
// add array of elements
var e = div([h1(),span()])
console.log(e.toString())
// context
var e = div(function(){
   h1()
   span()
})
console.log(e.toString())
<div><span></span></div>
<div><h1></h1><span></span></div>
<div><h1></h1><span></span></div>
<div><h1></h1><span></span></div>
Using element.add

Child nodes can be added using elem.add:

  • add a single element
  • add multiple elements
  • add array of elements
  • add array of elements
  • put a single element
  • using context node
const {div,span,h1} = require("dommini").tags
// add one element
var e = div().add(span())
console.log(e.toString())
// add elements
var e = div().add(h1(),span())
console.log(e.toString())
// add array of elements
var e = div().add([h1(),span()])
console.log(e.toString())
// context
var e = div().add(function(){
   h1()
   span()
})
console.log(e.toString())
<div><span></span></div>
<div><h1></h1><span></span></div>
<div><h1></h1><span></span></div>
<div><h1></h1><span></span></div>

Instead of add, one can put an element. The difference between add and put:

  • elem.add(child) return elem
  • elem.put(child) return child
const {div,span} = require("dommini").tags
var d = div()
var a = d.add(span())
console.log(a.toString()) // <div><span></span></div>
var s = d.put(span())
console.log(s.toString()) // <span></span>
<div><span></span></div>
<span></span>
Using context

Using elem.add or elem.context method, all elements directly created inside given callback are added to the context element:

const {div,span,h1} = require("dommini").tags
var e = div().context(function(){
   h1()
   span()
})
console.log(e.toString())
<div><h1></h1><span></span></div>

Context

Using a function as a parameter of element constructor, elem.add or elem.context methods, the element is considered as the context element. This means, that:

  • utils.attr and utils.addClass functions are applied to that node
  • all newly created "free" elements are added to the context element (in the order of creation)
const {tags,utils} = require("dommini")
const {section,div,span,h1} = tags
const {attr,addClass} = utils
var e = section()
e.context(function(){
   attr({id:"context",class:"cls"}) // attr of the context
   addClass("cls-2") // add class to the context
   var d = div().class("free") // "free" element
   var s = span().class("not-free") // NOT "free" element, added to "d"
   d.add(h1(),s)
})
console.log(e.toString())
<section id="context" class="cls cls-2">
  <div class="free">
    <h1></h1>
    <span class="not-free"></span>
  </div>
</section>

Context can also be nested

const {tags,utils} = require("dommini")
const {section,div,span,h1} = tags
const {attr,addClass} = utils
var e = section().context(function(){
   attr({id:"section-1"})
   h1("section 1")
   div(function(){
      attr({id:"subsection-1.1"})
      h1("subsection 1.1")
      span()
   })
})
console.log(e.toString())
<section id="section-1">
  <h1>section 1</h1>
  <div id="subsection-1.1">
    <h1>subsection 1.1</h1>
    <span></span>
  </div>
</section>

The concept is mainly useful for more complex code:

  • conditions and cycles
  • external functions
  • ...
const {tags,utils} = require("dommini")
const {main,section,div,span,h1,h2,a} = tags
const {attr,addClass} = utils

function box(title,content,level=1) {
   if (level === 1) {
      var sec = section(h1(title)).put(div(content))
   } else if (level === 2) {
      var sec = div(span(h2(title))).put(span(content))
   } else {
      var sec = span(title)
   }
   sec.context(function(){
      a(`see ${title}`).attr({href:`http://${title}`})
      if (title.endsWith("3")) addClass("title3")
   })
   return sec
}

var e = main(function(){
   box("main title",box("sub-sub-title","sub-sub-content",3))
   var subs = ["sub1","sub2","sub3"]
   subs.forEach(s => box(s,s.repeat(2),2))
})

console.log(e.toString())
<main>
  <section>
    <h1>main title</h1>
    <div>
      <span>
        sub-sub-title
        <a href="http://sub-sub-title">see sub-sub-title</a>
      </span>
      <a href="http://main title">see main title</a>
    </div>
  </section>
  <div>
    <span>
      <h2>sub1</h2>
    </span>
    <span>
      sub1sub1
      <a href="http://sub1">see sub1</a>
    </span>
  </div>
  <div>
    <span>
      <h2>sub2</h2>
    </span>
    <span>
      sub2sub2
      <a href="http://sub2">see sub2</a>
    </span>
  </div>
  <div>
    <span>
      <h2>sub3</h2>
    </span>
    <span class="title3">
      sub3sub3
      <a href="http://sub3">see sub3</a>
    </span>
  </div>
</main>

Extending

Element class can be easily extended

const {Element,tags} = require("dommini")
Element.prototype.href = function(address) {
   return this.attr("href",address)
}
var e = tags.a().href("https://github.com/")
console.log(e.toString())
<a href="https://github.com/"></a>

Other

parent and children

Elements have parent and children attributes:

const {div,span,h1} = require("dommini").tags
var h = h1()
var s = span()
var d = div(h,s)
console.log(d.parent) // null
console.log(s.parent) // Element{_tag:'div',...}
console.log(s.children) // []
console.log(d.children) // [Element{_tag:'h1',...},Element{_tag:'span',...}]
remove

Node can be removed if needed.

const {div,span} = require("dommini").tags
var d = div()
var s = d.put(span())
console.log(d.toString()) // <div><span></span></div>
s.remove()
console.log(d.toString()) // <div></div>
clone

Elements can be cloned.

const {div,span} = require("dommini").tags
var d = div()
var s = span().id("span1")
d.add(s)
d.add(s) // s is removed before adding! so d has only 1 child
console.log(d.toString())
d.add(s.clone().id("span2")) // now it has 2 children
console.log(d.toString())
<div><span id="span1"></span></div>
<div><span id="span1"></span><span id="span2"></span></div>

Document

dommini.Document represents the complete document. it is created as new Document(attrs), where attrs is an object with string values and keys:

  • doctype="html"
  • lang="en"
  • charset="utf-8"
  • title
  • keywords
  • author
  • "application-name"
  • description
  • generator="dommini {version}" Values are defaulted or optional.
const {Document} = require("dommini")
var doc = new Document({keywords:"dommini js"})
console.log(doc.toString())
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="keywords" content="dommini js">
    <meta name="generator" content="dommini 0.1.0">
  </head>
  <body></body>
</html>

html, head, body

html, head and body elements are attributes of the Document instance

const {Document,tags} = require("dommini")
const {div,span,script} = tags
var doc = new Document({generator:undefined})
doc.html.attr("manifest","/example.appcache")
doc.head.add(script())
doc.body.context(function(){
   div()
   span()
})
console.log(doc.toString())
<!DOCTYPE html>
<html manifest="/example.appcache" lang="en">
  <head>
    <meta charset="utf-8">
    <script></script>
  </head>
  <body>
    <div></div>
    <span></span>
  </body>
</html>

Attributes

Similarly to element.attr, document.attr() works as a getter and document.attr(...) can set attributes as key-value two arguments or as an object of key-value pairs. Have a look at Document construction for available attributes. In fact, you can set anything to the Document, but only the implemented stuff is meaningful and will have effect. To create a (not yet) unsupported meta input, you can always create a meta element in document.head.

const {Document,tags} = require("dommini")
var {meta} = tags

var doc = new Document()
doc.attr({
   description:"document.attr",
   title:"attr",
   whatever: "anything",
})
doc.head.add(meta().attr({name:"some-name",content:"some-content"}))
console.log(doc.attr())
console.log(doc.toString())
{
  doctype: 'html',
  lang: 'en',
  title: 'attr',
  charset: 'utf-8',
  keywords: '',
  author: '',
  'application-name': '',
  description: 'document.attr',
  generator: 'dommini 0.1.0',
  whatever: 'anything'
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="description" content="document.attr">
    <meta name="generator" content="dommini 0.1.0">
    <title>attr</title>
    <meta name="some-name" content="some-content">
  </head>
  <body></body>
</html>

Other

Document can be cloned.

const {Document} = require("dommini")
var doc = new Document()
var doc2 = doc.clone()
console.log(doc.toString() === doc2.toString()) // true
doc.attr({title:"title"})
console.log(doc.toString() === doc2.toString()) // false

TODO

  • more DOM API functionality (insertAfter,...)?
  • pretty printing?

Contributing

is welcome :-)

Maintainer

Jan Stránský

Acknowledgement

  • inspired by dominate python package
    • the context-defined adding of new elements)
    • partially this README and some examples

License

MIT