put-selector: CSS Selector-Based DOM Element Creation and Manipulation

By on September 29, 2011 1:05 am

The put-selector package provides a small yet powerful function for creating and manipulating the DOM through brief, familiar CSS selector syntax. CSS selectors are well understood by most front-end developers, used both for CSS and element selection through query engines. This function leverages this syntax for creating and updating elements quickly and efficiently. For example, to use the provided put() function to create a div with a class name of “foo” and append it to a parent element, we could write:

put(parent, "div.foo");

We can also update existing elements, here we add the class “updated” to an element:

put(element, ".updated");

We can leverage various combinations of CSS types, combinators, classes, reference elements to create and update elements in virtually limitless fashion with succinct, beautiful syntax. The package documentation provides all the details on different usages. The put-selector package can be found in the Dojo Foundation package repository and quickly downloaded from github or installed with CPM:

cpm install put-selector

Philosophy of Simplicity

The put() function was designed as a single multi-purpose tool, driven by a couple of key goals. First, the put() provides an entire library worth of DOM manipulation capabilities in a tiny codebase, 1.5KB minified and about 0.7KB gzipped. With put() we can create elements, delete elements, add classes, remove classes, set attributes, remove attributes, traverse elements, add text nodes, and more by just simply apply selectors to elements in different ways. This elegantly provides an enormous value for the byte count.

However, simplicity is more than just a small byte count. The even greater benefit is the reduced mental load for developers. Most developers spend a significant amount of their time learning APIs, looking up values in APIs, and debugging misused APIs. With put(), very little needs to be learned or recalled. The API is basically just leveraging the familiar rules of CSS selectors to create and modify existing elements. As developers, we all have a limited mental capacity (even though we don’t like to admit it). Using CSS selector syntax for element manipulation allows us to focus on getting work done rather than memorizing and messing with a large set of APIs.

Performance

In years past, many developers were conditioned to use innerHTML due to better performance. However, the times they are a changin’. While earlier versions of IE could create elements much faster with innerHTML than through DOM APIs, the targets of optimization, mobile devices, behave dramatically different. Typically running on WebKit, most mobile devices are running on modern browsers which actually perform faster with DOM APIs than with innerHTML. It no longer makes sense to optimize based on legacy hacks, and is in fact being counter-productive.

The put() function uses the most advanced techniques for optimal DOM manipulation. When possible, it keeps elements disconnected until all operations are complete. It also uses document fragments for multiple insertions when possible. Combined, these techniques mean very high-performance DOM create and update. We’ve created a jsperf test comparing simple single element creation with put() vs jQuery’s html() function (that uses innerHTML). And another test comparing a more complex set of elements being inserted using put() vs popular HTML templating libraries (jQuery tmpl and Mustache).

Selector-Based Creation vs HTML Templating

One of the most common ways to create DOM elements is through HTML templating. This goes hand in hand with innerHTML-driven DOM generation. HTML templating is often advocated on the basis of separation of concerns; keeping the view separate from logic. However, this approach is often ground on the fallacy of equating distinct syntax with distinct purpose. In reality, HTML is not a “view” language or technology. CSS was introduced as a “view” language to remove the presentational information from HTML, distinct from the semantics of HTML. Typical use of HTML templating treats HTML as the view of component, falling for the same language abuse.

In reality, divisions of functionality do not necessarily fall along the HTML and JavaScript distinction. Separation of concerns should be dictated by the natural organization of a component or application’s functionality and not the arbitrary constraints of the underlying language. With this in mind, we can often have much better code organization when DOM creation and updating can exist in the context of the code that is utilizing and driving the DOM interaction. Using a JavaScript API allows us to maintain this context and permit functionality-driven organization while still utilizing appropriate efficient language.

Also, within the context of JavaScript, element interaction can leverage the limitless capabilities of a true programming language. As needs become more complicated, template languages typically grow more and more complicated with ever increasing sets of constructs for conditionals, loops, and more. When creating elements within JavaScript, no new constructs are needed, JavaScript provides every construct you could ever need.

However, one of the great benefits of HTML templating is easy insertion of properly escaped data through variable substitution. The put() function actually provides variable substitution. This means you can still enjoy safe injection of text with the great syntax of put().

Server-Side: put() in NodeJS

While put() directly interacts with the DOM in the browser, put() can also be used on the server-side to generate HTML! In Node.js, where the DOM is not available, put() will automatically create a fast, light psuedo-DOM designed to generate HTML strings. Furthermore, this pseudo-DOM supports on-the-fly streaming of the HTML, so element creation can be directly piped to a response stream. This is not only more responsive (data can immediately start streaming to clients), but this also eliminates the need to retain DOM structures in memory, reducing the memory footprint.

Here is a simple example of how we could create a page in Node.js:

var http = require('http');
var put = require('put-selector');
http.createServer(function (req, res) {
	res.writeHead(200, {'Content-Type': 'text/html'});
	// create an HTML page, and pipe to the response 
	var page = put.Page(res); 
	// each element created is immediately sent to the stream
	put(page, 'head script[src=app.js]+title', 'Page Title');
	put(page, 'body div.content', 'Hello, World');
	// close all the tags, and end the stream
	page.end(); 
}).listen(80);

With Node.js support, we can enjoy the compact, consistent CSS selector syntax on both the client and server-side.

Conclusion

I’ve been using put() on several projects now (including the new dgrid), and love using it. It makes common DOM tasks faster and easier to write and read. Since mentioning this project on Twitter, it has quickly taken, gathering over 100 github repo watchers in a week. This is an exciting new Dojo Foundation package, and provides a simple, powerful approach to DOM creation and modification with familiar clean syntax.

Other posts in the series

  1. Dojo Foundation Packages
  2. Git-Linked Packages for NPM/Node
  3. ComposeJS: Robust, Lightweight Object Composition
  4. put-selector: CSS Selector-Based DOM Element Creation and Manipulation

Comments