Products manager with TABLE

by mporras

HTML

<table>
    <theah>
        <td>&nbsp;</td>
        <th colspan=2>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=invertSelection type=button>Toggle selection</button>
</p>

CSS

.action {
    cursor:pointer;
}

.action.save{
    background:rgba(0,255,0,0.3);
    margin-right:10px;
}

.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;
    outline:none;
}

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

JavaScript

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

for (var i = 1; i <= 10; 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 save" type=button title=Save>☺</button>'
        + '<button class="action delete" type=button title=Delete>X</button>'
        + '</td>'
        + '</tr>'
    );
}

$(':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();

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

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

$('button#invertSelection').click(function() {
    $(':input[type=checkbox]').each(function() {
        var...