Botond Balázs
Botond Balázs

Reputation: 2500

How to implement "add new item" functionality in JavaScript?

The title isn't very descriptive but I couldn't find a better one. Feel free to edit it.

Basically what I'm looking for is the best way to do the following:

add new item

When the user clicks "Add New Item", a new row is added with an indentical text box and drop down as above. The options I can think of are the following:

What do you suggest? I'm not completely satisfied with any of the above solutions.

Upvotes: 2

Views: 3441

Answers (4)

David Thomas
David Thomas

Reputation: 253318

While I realise you already have an accepted answer, I thought I'd offer a plain JavaScript means of achieving the same:

function closest(el, tag) {
    if (!el || !tag) {
        return false;
    }
    else {
        var curTag = el.tagName.toLowerCase();
        return curTag == tag.toLowerCase() && curTag !== 'body' ? el : closest(el.parentNode, tag);
    }
}

function addRow(el) {
    if (!el) {
        return false;
    }
    else {
        var tr = closest(el, 'tr').previousElementSibling,
            newRow = tr.cloneNode(true);
        tr.parentNode.insertBefore(newRow, tr.nextSibling);
    }
}

document.getElementById('add').onclick = function() {
    addRow(this);
}​

JS Fiddle demo.

Revised the above a little, to add a simple shim to cope with those browsers that don't implement previousElementSibling:

function closest(el, tag) {
    if (!el || !tag) {
        return false;
    }
    else {
        var curTag = el.tagName.toLowerCase();
        return curTag == tag.toLowerCase() && curTag !== 'body' ? el : closest(el.parentNode, tag);
    }
}

function prevElementSiblingShim(el) {
    if (!el) {
        return false;
    }
    else {
        var prevSibling = el.previousSibling;
        return prevSibling.nodeType == 1 ? prevSibling : prevElementSiblingShim(prevSibling);
    }
}

function addRow(el) {
    if (!el) {
        return false;
    }
    else {
        var par = closest(el, 'tr'),
            tr = par.previousElementSibling || prevElementSiblingShim(par),
            newRow = tr.cloneNode(true);
        tr.parentNode.insertBefore(newRow, tr.nextSibling);
    }
}

document.getElementById('add').onclick = function() {
    addRow(this);
}​

References:

Upvotes: 1

Brad Christie
Brad Christie

Reputation: 101604

Assuming the super-simple approach and that your format is in a table:

<table>
    <tr>
        <td><input type="text" name="item_name" placeholder="item name" /></td>
        <td><select name="item_type"><option value="" selected="selected">Type</option></select></td>
    </tr>
    <tr>
        <td><input type="text" name="item_name" placeholder="item name" /></td>
        <td><select name="item_type"><option value="" selected="selected">Type</option></select></td>
    </tr>
    <tr>
        <td colspan="2" id="add">+ Add new item</td>
    </tr>
</table>

You can use the following:

$('#add').on('click',function(e){
    var $this = $(this);
    var $newRow = $this.closest('table').find('tr:first').clone();
    $newRow.find(':input').val('');
    $newRow.insertBefore($this.parent());
});

Broken down:

  1. We give the last item an ID to make it easier to bind a click event to.
  2. Use jQuery and bind the click event to that ID which:
    • Grabs the current table we're clicking within ($this.closest('table'))
    • Locates the first row within that table and duplicates it (.clone())
    • Remove any populated values that may be present (.find(':input').val(''))
    • Append this new cloned row to the table just above the "add new item" row ($newRow.insertBefore(...))

You can also take the template approach, but that's really up to you and how much control you'd like over the output.

Upvotes: 1

Wouter van Nifterick
Wouter van Nifterick

Reputation: 24086

If you're already using a framework like jquery or sencha on your page, you might as well make use of it.

Otherwise, i'd keep it as simple as possible. This does't look like a very important core functionality, so don't create something that requires extra https requests or even entire libraries to be loaded. Cloning or generating the html might not look elegant, but it'll be:

  • fast to implement
  • easy to read, and therefore easy to debug
  • waste few resources, and extremely fast
  • stable
  • doesn't need server side code or extra http requests
  • it'll be easy to change the implementation later on if needed

Don't create something that's overkill for somerhing as trivial as this.

Upvotes: 1

Diodeus - James MacFarlane
Diodeus - James MacFarlane

Reputation: 114367

Inserting HTML from a string into the DOM is the most efficient way. Unless you're inserting hundreds at once, it doesn't really matter.

Upvotes: 0

Related Questions