• Stars
    star
    290
  • Rank 142,981 (Top 3 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 13 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A high-performance, lightweight function for creating and manipulating DOM elements with succinct, elegant, familiar CSS selector-based syntax

This put-selector/put module/package provides a high-performance, lightweight (~2KB minified, ~1KB gzipped with other code) function for creating and manipulating DOM elements with succinct, elegant, familiar CSS selector-based syntax across all browsers and platforms (including HTML generation on NodeJS). The single function from the module creates or updates DOM elements by providing a series of arguments that can include reference elements, selector strings, properties, and text content. The put() function utilizes the proven techniques for optimal performance on modern browsers to ensure maximum speed.

Installation/Usage

The put.js module can be simply downloaded and used a plain script (creates a global put() function), as an AMD module (exports the put() function), or as a NodeJS (or any server side JS environment) module. It can also be installed with CPM:

cpm install put-selector

and then reference the "put-selector" module as a dependency. or installed for Node with NPM:

npm install put-selector

and then:

put = require("put-selector");

Creating Elements

Type selector syntax (no prefix) can be used to indicate the type of element to be created. For example:

newDiv = put("div");

will create a new <div> element. We can put a reference element in front of the selector string and the <div> will be appended as a child to the provided element:

put(parent, "div"); 

The selector .class-name can be used to assign the class name. For example:

put("div.my-class") 

would create an element <div class="my-class"> (an element with a class of "my-class").

The selector #id can be used to assign an id and [name=value] can be used to assign additional attributes to the element. For example:

newInput = put(parent, "input.my-input#address[type=checkbox]");

Would create an input element with a class name of "my-input", an id of "address", and the type attribute set to "checkbox". The attribute assignment will always use setAttribute to assign the attribute to the element. Multiple attributes and classes can be assigned to a single element.

The put function returns the last top level element created or referenced from a selector. In the examples above, the newly create element would be returned. Note that passing in an existing node will not change the return value (as it is assumed you already have a reference to it). Also note that if you only pass existing nodes reference, the first passed reference will be returned.

Modifying Elements

One can also modify elements with selectors. If the tag name is omitted (and no combinators have been used), the reference element will be modified by the selector. For example, to add the class "foo" to element, we could write:

put(element, ".foo"); 

Likewise, we could set attributes, here we set the "role" attribute to "presentation":

put(element, "[role=presentation]");

And these can be combined also. For example, we could set the id and an attribute in one statement:

put(element, "#id[tabIndex=2]");

One can also remove classes from elements by using the "!" operator in place of a ".". To remove the "foo" class from an element, we could write:

put(element, "!foo");

We can also use the "!" operator to remove attributes as well. Prepending an attribute name with "!" within brackets will remove it. To remove the "role" attribute, we could write:

put(element, "[!role]");

Deleting Elements

To delete an element, we can simply use the "!" operator by itself as the entire selector:

put(elementToDelete, "!");

This will destroy the element from the DOM, using either parent innerHTML destruction (IE only, that reduces memory leaks in IE), or removeChild (for all other browsers).

Creating/Modifying Elements with XML Namespaces

To work with elements and attributes that are XML namespaced, start by adding the namespace using addNamespace:

put.addNamespace("svg", "http://www.w3.org/2000/svg");
put.addNamespace("xlink", "http://www.w3.org/1999/xlink");

From there, you can use the CSS3 selector syntax to work with elements and attributes:

var surface = put("svg|svg[width='100'][height='100']");
var img = put(surface, "svg|image[xlink|href='path/to/my/image.png']");

Text Content

The put() arguments may also include a subsequent string (or any primitive value including boolean and numbers) argument immediately following a selector, in which case it is used as the text inside of the new/referenced element. For example, here we could create a new <div> with the text "Hello, World" inside.

newDiv = put(parent, "div", "Hello, World");

The text is escaped, so any string will show up as is, and will not be parsed as HTML.

Children and Combinators

CSS combinators can be used to create child elements and sibling elements. For example, we can use the child operator (or the descendant operator, it acts the same here) to create nested elements:

spanInsideOfDiv = put(reference, "div.outer span.inner");

This would create a new span element (with a class name of "inner") as a child of a new div element (with a class name of "outer") as a child of the reference element. The span element would be returned. We can also use the sibling operator to reference the last created element or the reference element. In the example we indicate that we want to create sibling of the reference element:

newSpan = put(reference, "+span");

Would create a new span element directly after the reference element (reference and newSpan would be siblings.) We can also use the "-" operator to indicate that the new element should go before:

newSpan = put(reference, "-span");

This new span element will be inserted before the reference element in the DOM order. Note that "-" is valid character in tags and classes, so it will only be interpreted as a combinator if it is the first character or if it is preceded by a space.

The sibling operator can reference the last created element as well. For example to add two td element to a table row:

put(tableRow, "td+td");

The last created td will be returned.

The parent operator, "<" can be used to reference the parent of the last created element or reference element. In this example, we go crazy, and create a full table, using the parent operator (applied twice) to traverse back up the DOM to create another table row after creating a td element:

newTable = put(referenceElement, "table.class-name#id tr td[colSpan=2]<<tr td+td<<");

We also use a parent operator twice at the end, so that we move back up two parents to return the table element (instead of the td element).

Finally, we can use the comma operator to create multiple elements, each basing their selector scope on the reference element. For example we could add two more rows to our table without having to use the double parent operator:

put(newTable, "tr td,tr td+td");

Appending/Inserting Existing Elements

Existing elements may be referenced in the arguments after selectors as well as before. If an existing element is included in the arguments after a selector, the existing element will be appended to the last create/referenced element or it will be inserted according to a trailing combinator. For example, we could create a <div> and then append the "child" element to the new <div>:

put("div", child);

Or we can do a simple append of an existing element to another element:

put(parent, child);

We could also do this more explicitly by using a child descendant, '>' (which has the same meaning as a space operator, and is the default action between arguments in put-selector):

put(parent, ">", child);

We could also use sibling combinators to place the referenced element. We could place the "second" element after (as the next sibling) the "first" element (which needs a parent in order to have a sibling):

put(first, "+", second);

Or we could create a <div> and place "first" before it using the previous sibling combinator:

put(parent, "div.second -", first);

The put() function takes an unlimited number of arguments, so we could combine as many selectors and elements as we want:

put(parent, "div.child", grandchild, "div.great-grandchild", gggrandchild);

Variable Substitution

The put() function also supports variable substitution, by using the "$" symbol in selectors. The "$" can be used for attribute values and to represent text content. When a "$" is encountered in a selector, the next argument value is consumed and used in it's place. To create an element with a title that comes from the variable "title", we could write:

put("div[title=$]", title);

The value of title may have any characters (including ']'), no escaping is needed. This approach can simplify selector string construction and avoids the need for complicated escaping mechanisms.

The "$" may be used as a child entity to indicate text content. For example, we could create a set of <span> element that each have content to be substituted:

put("span.first-name $, span.last-name $, span.age $", firstName, lastName, age);

Assigning Properties

The put() function can also take an object with properties to be set on the new/referenced element. For example, we could write:

newDiv = put(parent, "div", {
	tabIndex: 1,
	innerHTML: "Hello, World"
});

Which is identical to writing (all the properties are set using direct property access, not setAttribute):

newDiv = put(parent, "div");
newDiv.tabIndex = 1;
newDiv.innerHTML = "Hello, World";

NodeJS/Server Side HTML Generation

While the put() function directly creates DOM elements in the browser, the put() function can be used to generate HTML on the server, in NodeJS. When no DOM is available, a fast lightweight pseudo-DOM is created that can generate HTML as a string or into a stream. The API is still the same, but the put() function returns pseudo-elements with a toString() method that can be called to return the HTML and sendTo method to direct generated elements to a stream on the fly. For example:

put("div.test").toString() -> '<div class="test"></div>' 

To use put() streaming, we create and element and call sendTo with a target stream. In streaming mode, the elements are written to the stream as they are added to the parent DOM structure. This approach is much more efficient because very little needs to be kept in memory, the HTML can be immediately flushed to the network as it is created. Once an element is added to the streamed DOM structure, it is immediately sent to the stream, and it's attributes and classes can no longer be altered. There are two methods on elements available for streaming purposes:

element.sendTo(stream)

The sendTo(stream) method will begin streaming the element to the target stream, and any children that are added to the element will be streamed as well.

element.end(leaveOpen) 

The end(leaveOpen) method will end the current streaming, closing all the necessary tags and closing the stream (unless the argument is true).

The returned elements also include a put() method so you can directly add to or apply CSS selector-based additions to elements, for example:

element.put('div.test'); // create a &lt;div class="test">&lt;/div> as a child of element

Here is an example of how we could create a full page in NodeJS that is streamed to the response:

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

On the server, there are some limitations to put(). The server side DOM emulation is designed to be very fast and light and therefore omits much of the standard DOM functionality, and only what is needed for put() is implemented. Elements can not be moved or removed. DOM creation and updating is still supported in string generation mode, but only creation is supported in streaming mode. Also, setting object properties is mostly ignored (because only attributes are part of HTML), except you can set the innerHTML of an element.

Proper Creation of Inputs

Older versions of Internet Explorer have a bug in assigning a "name" attribute to input after it has been created, and requires a special creation technique. The put() function handles this for you as long as you specify the name of the input in the property assignment object after the selector string. For example, this input creation will properly work on all browsers, including IE:

newInput = put("input[type=checkbox]", {name: "works"});

Using on Different document

If you are using multiple frames in your web page, you may encounter a situation where you want to use put-selector to make DOM changes on a different HTML document. You can create a separate instance of the put() function for a separate document by calling the put.forDocument(document) function. For example:

put2 = put.forDocument(frames[1].document);
put2("div") <- creates a div element that belongs to the document in the second frame.
put("div") <- the original put still functions on the main document for this window/context 

License

put-selector is freely available under either the terms of the modified BSD license or the Academic Free License version 2.1. More details can be found in the LICENSE. The put-selector project follows the IP guidelines of Dojo foundation packages and all contributions require a Dojo CLA. If you feel compelled to make a monetary contribution, consider some of the author's favorite charities like Innovations for Poverty Action.

More Repositories

1

json-schema

JSON Schema specifications, reference schemas, and a CommonJS implementation
JavaScript
505
star
2

multi-node

Multi-node provides launching of multiple NodeJS processes for TCP/HTTP serving
JavaScript
491
star
3

node-promise

Promise utilities for Node
JavaScript
399
star
4

lmdb-js

Simple, efficient, ultra-fast, scalable data store wrapper for LMDB
JavaScript
358
star
5

msgpackr

Ultra-fast MessagePack implementation with extension for record and structural cloning / msgpack.org[JavaScript/NodeJS]
JavaScript
339
star
6

promised-io

Promise-based IO for JavaScript.
JavaScript
237
star
7

nodules

Asynchronous URL-based CommonJS module loader for Node with automated dependency resolution and module reloading
JavaScript
198
star
8

cbor-x

Ultra-fast CBOR encoder/decoder with extensions for records and structural cloning
JavaScript
176
star
9

alkali

Alkali is library for functional reactive data flows that drive native-based UI elements
JavaScript
162
star
10

compose

ComposeJS is a lightweight JavaScript library for object composition
JavaScript
127
star
11

xstyle

A declarative, reactive framework that extends CSS
JavaScript
98
star
12

cpm

CommonJS Package Manager
JavaScript
95
star
13

commonjs-utils

Utility modules for CommonJS
JavaScript
89
star
14

transporter

Transporter is JSGI application that serves modules to the browser with dependencies included and packaged in the CommonJS module transport format.
JavaScript
40
star
15

dbind

Data bindings for Dojo
JavaScript
29
star
16

weak-lru-cache

A cache using LRU and weak references to cache data in a way that works in harmony with garbage collection
JavaScript
28
star
17

persevere

The Persevere server features a secure RESTful JSON interface for data interaction and storage of dynamic data, JSONQuery/JSONPath querying, Comet-based real-time data notification through Rest Channels and Bayeux support, class-based based object-oriented business logic with data integrity and validation through JSON Schema in its server-side JavaScript environment.
JavaScript
21
star
18

patr

Promise-based asynchronous test runner for JavaScript
JavaScript
16
star
19

lmdbx-js

Wrapper for libmdbx
C++
15
star
20

ua-optimized

AMD plugin for UA-based optimization of feature-detection-has.js based modules
JavaScript
13
star
21

alkali-todo

TodoMVC written with Alkali
JavaScript
12
star
22

each

Multi-purpose iteration function
JavaScript
9
star
23

eslint-plugin-auto-import

ESLint plugin for automatically importing modules
JavaScript
9
star
24

commonjs-package-template

A template for packages in the Dojo foundation package repository
JavaScript
8
star
25

cpr

CommonJS Package Repository with Git integration
JavaScript
7
star
26

bindr

Bindr is a declarative functional reactive binding language
JavaScript
7
star
27

cobase

Composable data stores from reactive JavaScript Join, Transform, Index, and Reduce functions, built on Alkali & LevelDB
TypeScript
7
star
28

babel-plugin-transform-safely

A Babel plugin for transforming expressions with safe property access and modifications, checking parent existence
JavaScript
7
star
29

litmus

An viewer/editor for exploring Alkali variable data flow/dependencies, and their connection to visual DOM elements on a page
JavaScript
7
star
30

cbor-records

Registration for CBOR tag for records
6
star
31

json-tools

CommonJS tools for interaction with JSON and JSON-style structured data
JavaScript
5
star
32

ordered-binary

Representation of JavaScript primitives as a Buffer such that binary order matches natural order of primitives
JavaScript
5
star
33

caesium

Caesium is a collection of CSS tools, including an AMD CSS loader and builder.
JavaScript
4
star
34

node-commonjs

Modules implementing CommonJS specifications on top of Node
JavaScript
4
star
35

babel-plugin-transform-alkali

Babel plugin to transform reactive expressions using alkali.
JavaScript
3
star
36

splinterdb-js

Initial exploration of JS library for SplinterDB
JavaScript
3
star
37

msgpackr-extract

Node addon for string extraction for msgpackr
C++
3
star
38

jss

JSON Stylesheets
JavaScript
2
star
39

coffeetoes6

A very simple coffeescript to ES6 converter with minimal code modification without extra syntactic requirements (you will need to make fixes afterwards)
JavaScript
2
star
40

window-name

Cross-domain browser communication using the window.name transport
JavaScript
2
star
41

literally-typed

A literate form of TypeScript for embedding type information in documentation
JavaScript
2
star
42

xstyle-todo

TodoMVC on xstyle (and dstore)
JavaScript
2
star
43

cbor-extract

C++
2
star
44

persvr-org

Code/contents of persvr.org
JavaScript
1
star
45

backtrace-c

Print C/C++ level backtraces in NodeJS
1
star
46

mysql-store

A MySQL object store implementation for NodeJS
1
star
47

ddoc

dgrid based documentation
JavaScript
1
star
48

db-benchmark

JavaScript
1
star
49

presly

Why am I using Keynote and Powerpoint when I could use dojo.gfx?
JavaScript
1
star