Dead simple JSON/DOM data corridor.
JavaScript CSS
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


{json} → <html> → {json}

Bi-directional data binding without the fuss.

why you need corridor

Your data is in JSON, but your users interact with HTML. corridor shuttles your data between your JSON and your HTML.

In a nutshell, corridor lets you turn this:

<fieldset name="project">
  <input type="text" name="" value="~0.1.0" />
  <input type="text" name="" value="~2.1.0" />

Into this:

  "project": {
    "dependencies": {
      "foo": "~0.1.0",
      "bar": "~2.1.0"

And vice versa.

how corridor works

corridor is a runtime library, not a templating language. It runs in the browser, easily transferring your data both ways: from form fields to JSON and (importantly) back again.

corridor uses the name attributes of your HTML elements to determine how a given input contributes to a JSON representation. In most cases it'll just work, but if you need more flexibility, corridor's API options offer many points of customization.

Read the tutorial to get started, or skip to the API section for the details.

corridor's philosophy

The corridor project philosophy boils down to these points:

  • intelligent — it learns what to do by looking at the data on the ground.
  • unobtrusive — it's just one function, without dependencies or side-effects.
  • clear — corridor's code, tests, and issues are crisply documented.

Development of features, bugfixes and documentation are held to these ideals.

getting corridor

corridor is a single js file with no dependencies. You can get it in either of two ways:

$ npm install corridor

corridor tutorial

It would be great if users could just edit JSON directly. That way, your REST API would be all you'd need.

But unfortunately, your users interact with the Document Object Model (DOM) representation of your HTML. Which means that it's your job to figure out how to get these two views of the data to match.

The corridor library has only one function called corridor(). This function does one of two things:

  • extract data out of a DOM heirarchy (form elements), or
  • insert data into the DOM.

Let's take a look at how this works by using the practical example of a package.json file. We'll build out a single-page web app for manipulating a package.json file.

To skip to the outcome of this walkthrough, see the example.html file.

package.json example app

For a package.json file, you need at least the following data:

  • name - the name of the project.
  • version - the semantic version number of the project.

And you may also want the following fields:

  • keywords - an array of keywords for npm to find your package.
  • dependencies - a collection of package/version pairs.

In all, that would produce JSON something like this (values omitted):

  "name": "",
  "version": "",
  "keywords": [],
  "dependencies": {}

Now let's put together the UI for working with this data.

corridor fields

Let's start with the name field. Here's the HTML you'd need:

<input type="text" name="name" />

Let's try it out. Make sure you have the <input> HTML on a page and the corridor.js library included. Then you can call the corridor function with no arguments to extract all the data on the page.

corridor(); // returns {"name":""} (or whatever you've typed in the box).

The first argument to corridor is the root element for the data extraction. If you don't provide one, corridor will assume you meant to search down from the document root.

corridor data types

By default, corridor will automatically detect the type of the value for an element. But you can override this behavior by providing a data-type attribute.

Let's see how this applies to the keywords field of a package.json.

The HTML for the keywords field should look like this:

<textarea name="keywords" data-type="list"></textarea>

Here the data-type indicates that we have a list value. corridor will try to parse the text in the <textarea> as a list of items, and will output an array.

Let's give it a try! With the above <textarea> on a page, enter the text "abc, def" (no quotes). Then run corridor:

JSON.stringify(corridor(), null, 2);
// produces
  "name": "",
  "keywords": [

Supported data types include:

  • auto - default, automatically detects the type.
  • string - just treats value as a string.
  • boolean - always true or false.
  • number - parses string as a float.
  • list - parses value as a list.
  • json - treats value as valid JSON.

If you can't set data-type because your application already uses this attribute for something else, you can use data-opts instead. Here's how it would look with data-opts:

<textarea name="keywords" data-opts='{"type":"list"}'></textarea>

The data-opts attribute, when present, must contain valid JSON. Note that options specified in data-opts will override data-* attributes.

Take this input for example:

<input type="text" name="zip" data-type="string" data-opts='{"type":"number"}' />

In this case, corridor will treat the zip as a number.

corridor field nesting

corridor will use parent elements' names to create nested structures.

For example, say we wanted to have drop-down choices for the foo and bar dependencies. The corridor HTML for that would look something like this:

    <select name="">
      <option value="~1.1.0">foo: version 1</option>
      <option value="~2.0.0">foo: version 2</option>
    <select name="">
      <option value="~3.5.0">bar: version 3</option>
      <option value="~4.1.0">bar: version 4</option>

Running corridor() on this gives us:

  "dependencies": {
    "foo": "~1.1.0",
    "bar": "~3.5.0"

But it doesn't end there! Since both the foo and bar select boxes live under dependencies, giving a name to the fieldset would have the same effect:

<fieldset name="dependencies">
    <select name="foo">
      <option value="~1.1.0">foo: version 1</option>
      <option value="~2.0.0">foo: version 2</option>
    <select name="bar">
      <option value="~3.5.0">bar: version 3</option>
      <option value="~4.1.0">bar: version 4</option>

If you run corridor against this HTML, you'll get the same JSON listed above.

Merging works best for objects like the dependencies object we just looked at. But corridor can also merge arrays.

rich path names

In the last section we saw a rudimentary example of how to create nested data structures. The range of supported names is quite rich.

These are best explained by example. Let's say you wanted to add authors to your package.json form, with a separate input for each author.

The HTML for that might look like this:

    first author:
    <input type="text" name="authors[]" value="your name" />
    second author:
    <input type="text" name="authors[]" />
    third author:
    <input type="text" name="authors[]" />

The name attribute for each author input is authors[]. The trailing square brackets means that the input value should contribute to an array.

Running corridor on the above would give you JSON like this:

  authors: [
    "your name"

Notice that there's only one element in this array. By default, corridor makes an intelligent decision about wether to include empty values in the extracted data. See the API documentation for how exactly it decides.

You can override the decision algorithm by specifying a data-empty attribute. If you set empty to include, then the element's value will be included even if it's empty. If you set empty to omit, then it'll be left out of the data representation if empty.

Just like with the case from last section, here we could split up the parts of the name between the fieldset and the inputs. E.g.

<fieldset name="authors">
    first author:
    <input type="text" name="[]" value="your name" />

You can mix and match dot delimited paths and square brackets to create even richer structures.

<input type="text" name="stock.ticker[]symbols" value="BCOV AMZN" data-type="list" />

Produces this:

"stock": {
  "ticker": [
      "symbols": [

Whitespace around key names is stripped, but whitespace inside them is preserved. For example name=" foo bar " would produce an object with a foo bar property.

merging arrays

Merging arrays can be tricky, but in most cases corridor will make a good choice.

In the last section, we looked at an example where the authors array contains normal string values. But let's look at what happens when the values are more complex.

Consider this HTML:

<table data-name="company.employees[]">
    <td><input type="text" name="name" value="Bob" /></td>
    <td><input type="text" name="email" value="" /></td>
    <td><input type="text" name="name" value="Alice" /></td>
    <td><input type="text" name="email" value="" /></td>

For this, corridor() produces the following:

  "company": {
    "employees": [{
      "name": "Bob",
      "email": ""
      "name": "Alice",
      "email": ""

The reason this works is that corridor checks each field under an arry to see if it can be safely merged into the last one. So when it finds Bob's email, it knows that it can safely add this key to the preceding Bob object without destroying data.

But when it gets to Alice's name, it sees that it couldn't safely add the value. If it set the last object's name to Alice, then the name of Bob would be lost. So it creates a new element and sets its name instead.

For more information on array merging, and how you can control it, see the API documentation.

toggling sections

You can mark sections of your UI as being toggleable using the role option.

For example, say you wanted a checkbox to control whether keywords were going to be included in the output. The HTML for that might look like this:

<fieldset data-role="toggleable">
      <input type="checkbox" data-role="toggle" checked/>
      include keywords?
      keywords (list format):
      <textarea name="keywords" data-type="list"></textarea>

Adding the toggleable role to the <fieldset> signals to corridor that this section can be turned on and off. The checkbox with the role toggle controls it.

You can nest toggleable sections inside each other. In each case, the toggle that controls the toggleable container is the nearest child.

inserting data

So far, this tutorial has focused on explaining how data flows from HTML to JSON. But corridor is great at sending data the other way as well.

To insert data back into the DOM, call the corridor function with a root element and a data structure object.

corridor(document.body, {
  name: "foo",
  keywords: ["bar", "baz"]

corridor uses the same name and data-* attributes to determine where data values should be inserted.

expanding to fit

When you send data from JSON into HTML, there's a chance that there won't be enough room. This is especilly true when working with arrays.

Consider this input JSON:

  "company": {
    "employees": [{
      "name": "Bob",
      "email": ""
      "name": "Alice",
      "email": ""

And this HTML:

  <tr data-name="company.employees[]">
    <td><input type="text" name="name" /></td>
    <td><input type="text" name="email" /></td>

If you ran corridor in insert mode in this scenario, Alice would be lost since there's only one row for company.employees.

Fortunately, corridor can expand the DOM for you to make room for the extra elements. If you set data-expand to auto on a named element, corridor will duplicate it to make room for data that otherwise wouldn't fit.

Here's the same example again, with the data-expand attribute set:

  <tr data-name="company.employees[]" data-expand="auto">
    <td><input type="text" name="name" /></td>
    <td><input type="text" name="email" /></td>

After calling corridor(table, data), the HTML in the page will look like this:

  <tr data-name="company.employees[]" data-expand="auto">
    <td><input type="text" name="name" value="Bob" /></td>
    <td><input type="text" name="email" value="" /></td>
  <tr data-name="company.employees[]" data-expand="auto">
    <td><input type="text" name="name" value="Alice" /></td>
    <td><input type="text" name="email" value="" /></td>

Since the expand feature is more intrusive in its side-effects in the DOM, you must enable it explicitly either by setting the data-expand option, or in the options argument to the corridor() function.

corridor API

The corridor API consists of two major parts: the corridor() function itself, and the information in the HTML it uses to make decisions about how to operate.

corridor() function

The corridor function takes three parameters, all optional:

corridor([root], [data], [opts])

The parameters are:

  • root — The starting DOM element to search for named fields (defaults to document).
  • data — The plain JSON data object whose values are to be inserted.
  • opts — Additional options to inform how corridor makes decisions.

The presence of the second parameter, data, tells corridor whether it should extract data from the DOM or insert data into it.

extracting data

To extract data from the DOM, call corridor() without the second argument, or set it to null.


corridor(root, null, opts);
corridor(null, null, opts);

In extract mode, corridor will:

  • start at the root element,
  • find all named fields,
  • extract their values, and
  • return the plain JSON data object that results.

This is completely safe. No side-effects are produced as a result of this operation, just data extraction.

inserting data

To insert data into the DOM, call corridor() with an object as the second argument.


corridor(null, data);
corridor(root, data);
corridor(null, data, opts);
corridor(root, data, opts);

In insert mode, corridor will:

  • start at the root element,
  • find all named fields,
  • set their values according to the data object (if a match can be found).

This will modify the values of discovered named fields where they differ from the data object representation.


The opts argument, when present, affects how corridor behaves in two ways. First, any values you specify will override the defaults for field value calculations.

For example, say you set the type property to binary:

corridor(null, null, {type:'binary'});

This means that any fields without an explicit type declared will be coerced to binary values. Fields with an explicit type (either as data-type or in data-opts) will still use their specified type though.

Secondly, some options give hints to corridor's higher level behavior.

For example, the enabledOnly property controls whether corridor will operate on fields that are disabled by a toggleable parent. By default enabledOnly is set to true, meaning only enabled fields are included. You could set enabledOnly to false in the opts hash to tell corridor to ignore the effects of toggleables.

Available options are:

  • type - the type of the field (auto, string, boolean, number, list, or json)
  • empty - whether to include the value in the output if the field is empty (auto, include, or omit)
  • merge - strategy to use when merging arrays (auto, concat, or extend)
  • include - whether a non-form element should be considered for insert/extract (auto, always, or never)
  • extract - strategy for pulling a value from a non-form element when extracting (auto, value, text, or html)
  • insert - strategy for putting a value into a non-form element when inserting (auto, value, text, or html)
  • expand - whether to expand the DOM to accomodate data that otherwise wouldn't fit (never, auto)
  • role - what role this element plays (field, toggleable, toggle, expand)
  • enabledOnly - only include enabled elements for consideration during insert/extract (true, false)

Note that setting options via the opts param specifically affects the execution of the corridor function just once. Persistent options should be stored in the HTML.

type option

The type option indicates the kind of field this is. The type determines how corridor converts the string value of the form element into the data representation.

The recognized types are:

  • auto - automatically detect the correct type based on the value (default)
  • string - treat the value as a string
  • boolean - coerce this value to something true/false
  • number - parse this value as a number
  • json - leave this value as-is (will choke if it's not actually valid JSON)
  • list - parse this value as a list of values

When automatically detecting the type, corridor uses the following algorithm:

  1. attempt to parse the value as JSON, if successful, use this value
    • Note: this covers booleans and numbers as well as richer JSON structures.
  2. otherwise treat the value as a string.

The list type will never be auto detected.

empty option

The empty option indicates whether the value should be included in the output if its value is empty.

Choices are:

  • auto - automatically detect the appropriate behavior based on the circumstances (default)
  • include - include the value in the output (default)
  • omit - do not add the field at all

When empty is set to auto, corridor uses the following algorithm to between include and omit:

  • if the element has a required attribute, then choose include, otherwise,
  • if the element would contribute a value by appending to an array (ex: name="authors[]"), choose omit, otherwise,
  • if the element is a checkbox, choose omit.

If none of these conditions are met, then choose include. With this algorithim, most of the time an empty value will contribute to the output, except in cases where you probably expect it wouldn't.

merge option

The merge option indicates which merging strategy corridor should use when merging two arrays.

Choices are:

  • auto - intelligently choose whether to concatenate the arrays, or deep merge them (default)
  • concat - concatenate the arrays
  • extend - deep merge each pair of items

When in auto mode, the algorithm for choosing whether to concatenate or merge two arrays should work as follows:

  • if the original array is empty, choose concat, otherwise,
  • if the length of the other array is greater than one, choose concat (this is a strange case), otherwise,
  • determine if the last element of the original array can be safely merged with the first element of the second array, if so, recursively merge them, otherwise,
  • choose concat.

The algorithm for deciding whether an object can be safely merged into a base object is as follows:

  • if either argument is a primitive (not an object or array), return false, otherwise,
  • if either argument is an array, return true (arrays can always be safely merged), otherwise,
  • recursively check the keys of the other object, if any can't be safely merged, return false,
  • return true.

Both auto and concat are safe operations. In neither case is data lost. However, extend is potentially (likely) unsafe—with this strategy, data is easily clobbered.

In all cases, if either the original or other object is not an array, there is no ambiguity to resolve. When at least one argument is not array-like, the merge will produce an object such that no information is lost (other than what is specifically overwritten by colliding keys).

For example, if the original object is an array (["foo"]) and the other object is a non-array-like object ({"bar":"baz"}). then the outcome of the merge will be an object that keeps all data in tact ({"0":"foo","bar":"baz"}).

include option

The include option indicates whether a non-form element should be considered for insert/extract.

Choices are:

  • auto - intelligently choose whether to include the element (default).
  • always - always include the element.
  • never - never include the element.

When operating in auto mode, corridor uses the following algorithm to decide whether a non-form element should be considered for insert/extract:

  • if the element is a form field (input, textarea, select), include it, otherwise,
  • if the element has no children with name or data-name attributes, include it, otherwise,
  • exclude it.

Only elements included by this algorithm will contribute to extracted output or receive inserted data.

extract option

The extract option indicates how a value should be extracted from an element under consideration.

Choices are:

  • auto - intelligently choose the best way to get a value (default).
  • value - get the element's form value (value attribute, except for <select> elements).
  • text - get the element's textContent.
  • html - get the element's innerHTML.

When operating in auto mode, corridor uses the following algorithm to decide how to extract a value:

  • if the element is a form field, use value extraction, otherwise,
  • if the element has no child elements (only text), use text, otherwise,
  • if the element is a <pre> or <code> element, use text, otherwise,
  • use html.

If you set the extract option, it's much better to set it in the HTML specifically for a particular element. Most of the time, you'll want the auto detection.

insert option

The insert option indicates how a value should be inserted into an element under consideration.

Choices are:

  • auto - intelligently choose the best way to insert the value (default).
  • value - set the element's form value (value attribute, except for <select> elements).
  • text - set the element's textContent.
  • html - set the element's innerHTML.

When operating in auto mode, corridor uses the following algorithm to decide how to insert a value:

  • if the element is a form field, use value insertion, otherwise,
  • if the value appears to contain no HTML elements, use text, otherwise,
  • if the element is a <pre> or <code> element, use text, otherwise,
  • use html.

If you set the insert option, it's much better to set it in the HTML specifically for a particular element. Most of the time, you'll want the auto detection.

expand option

The expand option indicates whether corridor should make any attempt to expand the DOM to accomodate data that otherwise wouldn't fit.

Choices are:

  • never - do not add any elements to the DOM (default).
  • auto - intelligently choose the best way to expand the DOM if necessary.

When operating in auto mode, corridor uses the following algorithm to decide how to expand the DOM:

  • identify candidates for expansion; these are elements which:
    • have a name or data-name attribute,
    • don't a data-expand value of never,
    • have a name format that ends in [],
    • are not the children of such an element (the algorithm does not recurse)
  • for each set of candidates:
    • find the matching array from the source data,
    • if there source data array and list of candidate fields have the same length, abort, otherwise,
    • find the best target to clone, then,
    • create N clone siblings of the target, appending each sequentially to the target's parent element, where N is the difference between the data array length and the length of the list of candidate elements.

The algorithm for finding a clone target for a set of candidate elements is:

  • start with the last element in the list of candidates (called elem here),
  • if that element is not a value'd form field:
    • check for a child with data-role set to expand, if found, use it
  • if the element elem is a value'd form field:
    • walk up the DOM looking for a parent with data-role set to expand, if found, use it, otherwise,
    • walk up the DOM looking for either a <li> element or a <tr>, if found, use it
  • last resort, use the element elem

Known limitations:

  • Only the last candidate element is searched for a target to clone. It's not round-robin or based on the content of the data array.
  • The shortfall is calculated from the element which starts the process, not the target element.
  • The algorithm doesn't recurse, so nested expand elements will not be expanded.

Improving on these limitations will require a major update to the code base to include an intermediate tree structure. This tree structure would provide the rich information necessary to make more intelligent auto-expand decisions.

See Issue #39.

toggle options

Options specific to the toggleable/toggle functionality are:

  • role — The role that this element plays in corridor operations. Choices are:
    • field - this element is a field whose value will contribute to extracted data (default)
    • toggleable - this element contains fields and may be toggled on or off
    • toggle - this element is a checkbox which toggles its nearest parent toggleable
  • enabledOnly — (boolean) When inserting/extracting, only operate on enabled fields (default: true).


corridor inspects the Document Object Model (DOM) at runtime to figure out how to extract and insert data. Specificially, it looks at these things:

  • the tag name,
  • the name (or data-name) attribute, and
  • the data-* (or data-opts) attributes.

The tag name influences whether corridor considers an element to have a value, and if so, how to retrieve it. For instance, the way you extract a value from a <textarea> differs from how you extract a value from a <select> element.

The name attribute is by far the most important one to corridor. The presence of a name attribute (or data-name) tells corridor that an element should be considered for data insertion/extraction. The content of this attribute tells corridor exactly how to shuttle data between the element's value and the data representation.

The data-* attributes, when present, override the default options (see the opts section above).

name attribute

corridor uses the name attribute of an element to figure out how the value of that element relates to the data representation.

You can also use the data-name attribute instead. corridor will actually check the data-name attribute first and use it if present, falling back to plain name. This serves two purposes.

First, strictly speaking, not all HTML5 elements allow the name attribute. But HTML5 doel allow data- prefixed attributes on any element. If you want to assign a name to a p or a div tag, for example, you should use data-name.

Secondly, data-name supplies an alternative should your application require that the name field has a specific value. If you need to keep name the same, but want corridor to address it by a different name value, you'd use data-name.

There are two formats you can use when specifying the name of an element: name format and field format.

Note: better names for "name format" and "field format" are forthcoming.

name format

The name format is the more natural of the two formats. In name format, the value resembles how you'd access a nested value inside an object in JavaScript.

For instance, say your JSON representation is this:

  "book": {
    "title": "The Art of War"

Then an input that maps to the title would have name="book.title":

<input type="text" name="book.title" value="The Art of War"/>

In name format, use periods to separate keys. They can nest to arbitrary depth, e.g. {"a":{"b":{"c":"foo"}}} maps to the element with name="a.b.c".

You can also use brackets to indicate a subkey (as opposed to using a period .). For example, the following are all equivalent to name="a.b.c":

  • name="[a][b][c]"
  • name="a[b]c"
  • name="a[b].c"
  • name="a.b[c]"

Whitespace is trimmed from the beginning and ending of keys, but not inside. So name="a b" is different from name="a b", but all of the following are equivalent to name="a.b.c":

  • name="[ a ][ b ][ c ]"
  • name=" a.b.c "
  • name="a[b]. c"
  • name="a. b[c]"

Finally, a pair of square brackets with nothing inside ([]) means that the value should contribute to an array. Consider this HTML:

<input type="text" name="book.authors[]" value="Sunzi"/>
<input type="text" name="book.authors[]" value="Giles, Lionel"/>

With corridor, this would map to the following data representation:

  "book": {
    "authors": [
      "Giles, Lionel"

Where you're appending to an array, you'll probably want the square brackets at the end, but this isn't strictly necessary. Your name attribute can have additional keys and bracket pairs after the first. Here are a few example names and the JSON data they'd map to:

// <input name="authors[]name" value="Sunzi" />
  "authors": [
    { "name": "Sunzi" }
// <input name="authors[][]" value="Sunzi" />
  "authors": [
    [ "Sunzi" ]
// <input name="[][author]" value="Sunzi" />
[ { "author": "Sunzi" } ]

In most cases, using name format for your name attributes will give you what you need to correctly shuttle data between your JSON and your HTML. However, if your JSON is quite complex, you may need to use field format for some of your elements.

field format

Note: field format will probably be removed in a future version of corridor

Whereas name format resembles how you'd access an object in JavaScript, field format resembles how you describe an object in JavaScript—that is, JSON.

With field format, you specify how your data should appear as properly formatted JSON. Except that you replace the value with the literal string $$$.

For example, consider the name format string book.title. The field format version would be {"book":{"title":$$$}}. Any name format string can be converted to field format, but the opposite is not always true.

Here are some name format strings and their field format equivalents:

  • title{"title":$$$}
  • book.title{"book":{"title":$$$}}
  • authors[]{"authors":[$$$]}
  • authors[]name{"authors":[{"name":$$$}]}
  • [][$$$]
  • a.b.c{"a":{"b":{"c":$$$}}}

You should use the name format for your name attributes.

data-opts attribute

Just as data-* attributes override the default options, data-opts can also be used to override options. If present, the data-opts attribute must contain valid JSON.

For example data-type="list" is the same as data-opts='{"type":"list"}'. When both a data-* attribute and a data-opts key have the same name (like in this list example) corridor will use the value in data-opts. This way, if your application already uses a data-* attribute that would conflict with corridor, you can use data-opts instead.

issues and feature requests

If you find any issues with corridor, or if you'd like to request a feature, please head over to the issues page on github.

Keep in mind that the more specific you are, the more likely your issue or feature is to be addressed.


If you have a question about how to use corridor, or if you're not sure if you're doing it right, go to stackoverflow and ask a question. Make sure you add the corridor tag to your question.

developing corridor

If you're interested in developing corridor, great! Start by forking corridor on github.

Once you've forked the project, clone it using git clone:

$ git clone<YOUR_USERNAME>/corridor.git

The source code for corridor itself is in the src/ directory.

corridor uses npm for packaging and deployment, so you'll need to install Node.js if you haven't already. Once you have Node, you can pull in corridor's development dependencies:

$ npm install

testing corridor

The corridor unit tests are in the test/ directory. corridor's unit tests are written to run in nodeunit or with QUnit.

After installing the npm dependencies, you can run the corridor unit tests from the command line like this:

$ npm test

To run the unit tests in the browser, just open test/index.html and they'll run automatically.

submitting changes

When you're satisfied with your changes, commit them and push them to your forked repository. Then open a pull request in github by hitting the big "Pull Request" button from the main project repo page.