Products manager with TABLE

by seefeld

HTML

<script src="http://documentcloud.github.com/underscore/underscore.js"></script>
<p>(Navigate with the arrow keys &uarr; &darr; and edit the product's name)</p>
<table>
    <theah>
        <td>&nbsp;</td>
        <th>Id</th>
        <th>Product</th>
        <td>&nbsp;</td>
    </theah>
    <tbody id=products></tbody>
</table>
<p style="margin-top:8px;">
    <button id=deleteSelected type=button>Delete selected</button>
    <button id=selectAll type=button>Select all</button>
    <button id=toggle type=button>Toggle</button>
</p>

CSS

.action
, input[type=checkbox] {
    cursor:pointer;
}

.action.delete {
    background:rgba(255,0,0,0.3);
}

tr {
    background:#F5F5F5;
}

tr:nth-child(even) {
    background:#E1E1E1;
}

tr:hover {
    background:#B2DBFC;
}

.productDescription {
    background:transparent;
    padding-left:4px;
}

input[type=text] {
    border:none;
}

JavaScript

// Populate products
var $products = $('tbody#products');

for (var i = 1; i <= 5; i++) {
    $products.append('<tr data-product-id=' + i + ' class=product>'
                     + '<td><input type=checkbox /></td>'
                     + '<td>' + i + '</td>'
                     + '<td><input class=productDescription type=text value="Product ' + i + '" /></td>'
                     + '<td><button class="action delete" type=button title=Delete>X</button></td>'
                     + '</tr>');
}

// Select text on enter and trigger arrow keys navigation
$(':input.productDescription').bind('focus', function() {
    $(this).select();
}).bind('keydown', function(e) {
    if (e.which === 40) {
        var $next = $(this).data('next');
        if ($next != null) {
            $next.select();
        }
    } else if (e.which === 38) {
        var $prev = $(this).data('prev');
        if ($prev != null) {
            $prev.select();
        }
    }
});

function chainNavigation() {
    var $productDescriptions = $(':input.productDescription');

    $productDescriptions.each(function(i) {
        $(this).data('next', $productDescriptions[i + 1]);
        $(this).data('prev', $productDescriptions[i - 1]);
    });
}

function deleteProduct(e) {
    var toDelete = [];

    e.each(function() {
        var $row = $(this).closest('tr');

        toDelete.push($row.data('productId'));

        $row.css('background', 'rgba(255,0,0,0.3)').fadeOut(function() {
            $(this).remove();
            chainNavigation();
        });
    });

    console.info('To delete: ' + JSON.stringify(toDelete));
}

chainNavigation();

// Inline delete buttons
$('button.action.delete').bind('click', function() {
    deleteProduct($(this).attr('disabled', true));
});

// Footer action buttons
$('button#selectAll').bind('click', function() {
    $(':input[type=checkbox]').attr('checked', true);
});

$('button#toggle').bind('click', function() {
    $(':input[type=checkbox]').each(function() {
    ...