Appendo » Form Row Cloning Plugin


Appendo is a lightweight plugin to manage cloning form rows.
Appendo is less than 2000 bytes and is very easy to use.


  • Creates an interface to manage adding cloned form rows
  • Remove button appears when added rows can be removed
  • Optionally limit the number of rows that can be added
  • Optionally focus first form field in added row
  • Multiple easy-to-use attachment methods (no Javascript required)
  • Lightweight, less than 2K! Well-commented source
  • Self-contained Javascript file (no other files needed)
  • Can be styled by CSS rules or via jQuery properties object
  • Completely namespaced as jQuery.appendo


Appendo aims to be a lightweight solution to duplicating form rows in a simple jQuery plugin format. The appendix includes some PHP example code for naming and reading rows.

Appendo also aims to be a succinct example of jQuery plugin design (see code).

Basic Usage

It's very simple to attach Appendo.
In fact, it doesn't even require any Javascript.

  • Load jQuery and Appendo Javascript files in your page <head>
  • Set up the row for duplication as the final row in a table
  • You may use header rows or other layout in the table above the row
  • Simply add class="appendo" to the table with to duplicate

If you've loaded jQuery and Appendo correctly, Appendo will automatically attach to tables with class="appendo". It's that easy!

Appendo Options

A reference for the most common options:

labelAdd string String value of "add" button
(default: 'Add Row')
labelDel string String value of "remove" button
(default: 'Remove')
maxRows integer Set to 0 for no limit
allowDelete boolean Delete button will be shown if true
copyHandlers boolean Set to true to copy event handlers
(default: false)
focusFirst boolean Focus 'input:first' on added form rows
(default: true)

Setting Options

There are two methods for setting Appendo options. Global Appendo options can be set to override defaults on all Appendo instances created henceforth. This can be done by inserting inline Javascript like so:

$.appendo.opt.maxRows = 5;
$.appendo.opt.labelAdd = 'Another';
jQuery.appendo.opt.copyHandlers = true;
jQuery.appendo.opt.focusFirst = true;

Note that both syntaxes refer to the same set of global options. When setting options globally, just insert them inline, don't wrap with $(document).ready() or similar. This way, you'll ensure they're set before Appendo is invoked automatically (if this is how you're invoking it).

The other method for setting options is using jQuery plugin syntax.

Plugin Syntax

Of course, Appendo can also be used as a typical jQuery plugin. You would usually do this if you wanted to set options on a per-instance basis, or if you have some other aversion to using a class name as per Appendo's automatic attachment method. It uses the same set of options listed above. An example invocation with options:

$(function() { 
        maxRows: 4,
        allowDelete: false,
        labelAdd: 'Add Another Row'

In this example, the table with id="objid" will be used.

Stylish Appendo

Appendo doesn't need any additional CSS files or settings, but some designers may wish to style the output to match their page. This information should help you do that. However, it can't teach you how CSS works; you'll need to understand at least the basics already.

Using Stylesheets

If you are styling via CSS rules included inline or in an external stylesheet, the styles for Appendo might look like this:

.appendoButtons { border: 1px black solid; }
.appendoButtons button { background: white; font-size: 80%; }

This div is inserted directly after the selected element in the DOM. The wrapClass option can be set if you want to change the class given to this button wrapper div.

wrapClass string Name of class to set on button wrapper div

Style Directly

An alternative method to perform CSS styling is to set an object for wrapStyle and/or buttonStyle options. Object(s) will be passed directly to jQuery's .css() method.

wrapStyle object jQuery CSS properties object to set on wrapper
(default: { padding: '.4em .2em .5em' })
buttonStyle object jQuery CSS properties object to set on buttons
(default: { marginRight: '.5em' })

If you set either option, you will override the default margins and padding (so you may need to incorporate that into your properties). If you use "stylesheet" rules and want to override the default margins and padding you may need to use !important or set an empty object for wrapStyle and buttonStyle options.

Callback Events

Appendo supports two callback events, settable as options:

onAdd callback Called when a row has been added
onDel callback Called before a row is removed (return true to delete)

Decorating Added Rows

When a new row is added, the onAdd callback is fired. If set, the function is called and given a jQuery object referencing the added element (usually a table row). This function is called at the end of the addition, after it's in the DOM, so it's even safe to .remove() if your callback function sees fit. It goes without saying that you can subselect and tweak the object to your heart's content with jQuery.

Examining Rows for Deletion

When the "remove" button is clicked, the onDel callback is fired. If set, the function is called and given a jQuery object referencing the element that will be removed (usually a table row). This function is called before removal. Your callback may inspect the object, take action(s), and return true or false, signaling whether the removal should continue. If a non-true value is returned, the removal will not take place. In this way, you can implement a "do you really want to lose this data?" prompt, or any other sort of behavior.


Why Tables?

Well, when you think about it, it's tabular data. If you absolutely hate tables, or you have another reason to use it somewhere else, there's an undocumented option called subSelect which is initially set to 'tr:last'. This way, the interface could be used to duplicate any selector within the outer selection.

<div id="outer">
  <div class="inner"> Making copies... </div>

So you could invoke Appendo like this:

$(function() { 
    $('#outer').appendo({ subSelect: '.inner' });

This could have interesting side effects if the subSelect were set to a selector that wasn't unique within the invoked selection.

Reading Multiple Rows in PHP

The naming of form fields in the cloned rows is beyond the scope of the Appendo plugin. However, if you're using PHP, or a language with similar "form-to-array" features, this isn't a problem. Consider this table with Appendo rows added:

<table class="appendo">
    <td><input type="text" name="col1[]" /></td>
    <td><input type="text" name="col2[]" /></td>
    <td><input type="text" name="col2[]" /></td>

To read these rows dynamically in PHP, you can do something like:

$rv = array();
foreach($_POST['col1'] as $k => $v)
    $rv[] = array($v, $_POST['col2'][$k], $_POST['col3'][$k]);

This is possible because PHP reads form values named with [] as arrays. So by looping over the first column, you can associate those values with the other columns.

Note: This is only tested on select elements and input elements with type other than radio and checkbox. I suspect it will not work as intended with those types of input.