0.4.0 • Published 6 years ago

dom-element-wrapper v0.4.0

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

DOM Element Wrapper

npm Software License Build Status Coverage Status

Motivation

This thin wrapper aims to provide a friendlier interface when it comes to creating nodes using the DOM API.

If like me, you enjoy chaining stuff and you feel sad when you have to use the DOM API, this library is made for you!

Limitation

The library relies on the Proxy object which is not yet supported by all browsers and cannot be transpiled or polyfilled.

Install

$ npm i --save dom-element-wrapper

or

$ yarn add dom-element-wrapper

Usage

The DOM API is extremely verbose and the way it was designed makes it very difficult to keep your code concise.

For instance, adding a single node to an existing one.

const element1 = document.querySelector(".my-container");
const element2 = document.createElement("div");
element2.id = "element-id";
element2.className = "css-class";

element1.appendChild(element2);

This can be replaced with the following syntax

import { wrap } from "dom-element-wrapper";

let element = document.querySelector(".my-container");

element = wrap(element).append(
  wrap("div", {
    id: "element-id",
    className: "css-class"
  })
);

The cool thing is you still have access to the underlying node, so you can do something like this:

import { wrap } from "dom-element-wrapper";

const nodes = wrap(element)
  .append(
    wrap("div", {
      id: "element-id",
      className: "css-class"
    })
  )
  .setAttribute("title", "Element's title")
  .addEventListener("mouseover", handler)
  .appendChild(document.createElement("div")).childNodes;

We are now able to chain the node's methods! Whoop Whoop!

You still have access to the element's properties, the only difference is: the proxy intercept "setters" methods that return a relevant result cannot be chained (.i.e such as querySelector, cloneNode, etc...) as so for methods starting with get has or is however methods that returns irrelevant results can be chained (.i.e such as addEventListener, insertBefore, appendChild, etc...).

What do I call irrelevant results?

For example, the method appendChild will return the appended node. Who cares really? This is typically what I call an irrelevant result.

But wait a minute, what if we want to build a DOM structure a bit more complicated?

Well for instance, instead of writing this:

const element = document.createElement("div");
element.className = "nice-stuff";

const div = document.createElement("div");
const p = document.createElement("p");

p.appendChild(document.createTextNode("List of stuff"));
div.appendChild(p);

element.appendChild(div);

const ul = document.createElement("ul");

const entries = ["Stuff 1", "Stuff 2", "Stuff 3", "Stuff 4"];

entries.forEach(function(entry) {
  const li = document.createElement("li");
  li.appendChild(document.createTextNode(entry));
  ul.appendChild(li);
});

element.appendChild(ul);

document.querySelector("body").appendChild(element);

you can simply write something like that:

import { wrap } from "dom-element-wrapper";

const entries = ["Stuff 1", "Stuff 2", "Stuff 3", "Stuff 4"];
const items = entries.map(entry => wrap("li").append(entry));

const element = wrap("div", { className: "nice-stuff" })
  .append(
    wrap("div").append(wrap("p").append("List of stuff")),
    wrap("ul").append(...items)
  )
  .unwrap();

document.querySelector("body").appendChild(element);

Both will create the same result.

<div class="nice-stuff">
  <div>
    <p>List of stuff</p>
  </div>
  <ul>
    <li>Stuff 1</li>
    <li>Stuff 2</li>
    <li>Stuff 3</li>
    <li>Stuff 4</li>
  </ul>
</div>

So what is this unwrap method all about?

When we wrap the DOM element by calling wrap, the element is being wrapped within a proxy object but if we try to append this proxy to a DOM element, it will fail. We need to revoke the proxy to reveal the underlying object, this is exactly what unwrap does.

It is worth noting that you only need to call unwrap when you need to pass the element to a method that expects a actual DOM element. If you only need to manipulate the element, unwrapping is not necessary.

API

Create a wrapper

Create the node and wrap it

const element = wrap("div", { id: "my-id" });
<div id="my-id"></div>

Wrap an existing node

const element = wrap(document.querySelector(".container"), {
  id: "my-id"
});
<div class="container" id="my-id"></div>

Prepend/append nodes

const element = wrap("div")
  .prepend(
    wrap("div", {
      id: "my-id"
    })
  )
  .prepend(
    wrap("div", {
      className: "my-style"
    })
  );
<div>
  <div class="my-style"></div>
  <div id="my-id"></div>
</div>
const element = wrap("div")
  .append(
    wrap("div", {
      id: "my-id"
    })
  )
  .append(
    wrap("div", {
      className: "my-style"
    })
  );
<div>
  <div id="my-id"></div>
  <div class="my-style"></div>
</div>

Prepend/append text nodes

const element = wrap("div")
  .prepend("world")
  .prepend(" ")
  .prepend("Hello");
<div>
  Hello world
</div>
const element = wrap("div")
  .append("Hello")
  .append(" ")
  .append("world");
<div>
  Hello world
</div>

Prepend/append wrappers

const element = wrap("div").prepend(wrap("h1"), wrap("h2"), wrap("p"));
<div>
  <p></p>
  <h2></h2>
  <h1></h1>
</div>
const element = wrap("div").append(wrap("h1"), wrap("h2"), wrap("p"));
<div>
  <h1></h1>
  <h2></h2>
  <p></p>
</div>

Reveal underlying node

const element = wrap("div").unwrap();

That's it!

Contributing

Install the dependencies

$ make

Run the test suite

$ make test

Run the linter

$ make lint

Format the source code

$ make fmt
0.4.0

6 years ago

0.3.0

6 years ago

0.2.0

7 years ago

0.1.0

7 years ago

0.0.4

7 years ago

0.0.3

7 years ago