This post shows how to write your own simple DOM utility, which may eliminate the need to include a big library just for it's convenient DOM functionality. This topic will be split across several posts. This is part 1.
Download code/resources for this post from GitHub
When I was first started learning front-end development, I often went straight to jQuery when I had to manipulate the DOM. I've found that it is simple enough to build your own DOM utility, unless you are relying on a library such as jQuery to support old browsers. Plus, I enjoy programming in JavaScript so writing my own DOM utility was a fun exercise for me!
Let's call our utility $DOM
. We want to create a 'class' to wrap
standard DOM elements so that they have useful methods available on them.
Start by defining the prototype of our $DOM
objects. For now we
will leave it empty.
$DOM_proto = {};
We want our main $DOM
function to allow creation of new elements
as well as wrapping standard DOM elements, as well as accepting selectors and
returning an array of $DOM
objects corresponding to the elements
matching a certain selector.
function $DOM(arg) {
// Throw error for invalid argument
if (!arg || !(typeof arg === 'string' || arg instanceof Element))
throw new Error('Invalid argument provided');
var newTagPattern = /^<([^\/]+)\/?>$/;
// Create new $DOM object
var $DOMobj = Object.create($DOM_proto);
if (arg instanceof Element) {
// Just wrap the element in with our $DOM methods
$DOMobj._element = arg;
return $DOMobj;
}
// Convert arg to string, trim off white space
arg = ('' + arg).trim();
// Arguments like '<tag>' should create a new empty element
if (newTagPattern.test(arg)) {
// Create a new element
$DOMobj._element = document.createElement(arg.match(newTagPattern)[1]);
return $DOMobj;
}
// Assume a selector was provided. Try to select all elements
// matching the selector. Throw error if selector is invalid.
var elem, $doms = [], i, n;
try {
elem = document.querySelectorAll(arg);
n = elem.length;
$doms = [];
for (i = 0; i < n; i++)
$doms.push($DOM(elem[i]));
return $doms;
} catch (exception) {
throw new Error('Invalid selector: ' + arg);
}
return null;
};
This gives us the following use cases for the $DOM
function:
// New empty div element
var newDiv = $DOM('<div>');
// Select all divs - leave out the tag brackets
var divs = $DOM('div');
// Select all elements of class 'box'
var boxes = $DOM('.box');
Notice our $DOM()
function returns objects with a single property
called _element
which stores the raw DOM element, and the prototype
is given by $DOM_proto
(or an array of such objects). To add
convenience methods to each object, we need to specify them on the
prototype. Let's get started with some simple methods.
Note: We want to make setter functions chainable. This is a popular way of writing code and is seen is libraries like jQuery and D3.js. Basically, we want to be able to do cool things like this:
var myDiv = $DOM('<div>')
.addClass('editable')
.attr('contentEditable', '')
.text('This paragraph is editable!');
get()
This method returns the raw DOM element. The _element
property should be treated as private, so we need to provide an
accessor function.
$DOM_proto.get = function get() {
return this._element;
};
text()
This method gets or sets the text content of the current element. The setter form is chainable.
$DOM_proto.text = function text() {
// Get text
if (arguments.length === 0)
return this._element.textContent;
// Set text, return this to make method chainable
this._element.textContent = '' + arguments[0];
return this;
};
html()
This method gets or sets the inner HTML of the current element. The setter form is chainable.
$DOM_proto.html = function html() {
// Get inner HTML
if (arguments.length === 0)
return this._element.innerHTML;
// Set innerHTML, return this to make method chainable
this._element.innerHTML = arguments[0];
return this;
};
attr()
This method gets or sets attributes of the current element. The setter is chainable.
$DOM_proto.attr = function attr() {
// Throw error when no argument provided
if (arguments.length === 0)
throw new Error('No attribute provided');
// Throw error when invalid argument provided
if (!arguments[0])
throw new Error('Invalid attribute provided');
// Get attribute
if (arguments.length === 1)
return this._element.getAttribute('' + arguments[0]);
// 2 arguments - set attribute. Return this (chainable)
this._element.setAttribute(arguments[0], arguments[1]);
return this;
};
prop()
This method gets or sets properties on the current element.
For example, to change an input value you would use this
method rather than the attr()
method.
The setter is chainable.
$DOM_proto.prop = function prop() {
// Throw error if no argument provided
if (arguments.length === 0)
throw new Error('No property specified');
// Throw error if invalid property name provided
if (!arguments[0] || typeof arguments[0] !== 'string')
throw new Error('Invalid property specified')
// Get property value
if (arguments.length === 1)
return this._element[arguments[0]];
// 2 arguments - set property. Return this (chainable)
this._element[arguments[0]] = arguments[1];
return this;
};
I hope you enjoyed this post! See Part 2 for the implementations of more useful methods.