Check the Checkbox

I often use the "add and remove rows from a grid" pattern to permit handling groups of data, but today I had to manage a particular use case.

Given a simple table

<table>
    <tr>
        <td>
            <input type="text" name="firstname[]">
        </td>
        <td>
            <input type="text" name="lastname[]">
        </td>
        <td>
            <input type="email" name="email[]">
        </td>
    </tr>
    <tr>
        <td>
            <input type="text" name="firstname[]">
        </td>
        <td>
            <input type="text" name="lastname[]">
        </td>
        <td>
            <input type="email" name="email[]">
        </td>
    </tr>
</table>

it is easy to iterate contents on server side after a POST, given the matching indexes of the firstname[], lastname[] and email[] arrays to recombine components of each row.

Things are going nuts when you have this other table

<table>
    <tr>
        <td>
            <input type="text" name="firstname[]">
        </td>
        <td>
            <input type="text" name="lastname[]">
        </td>
        <td>
            <input type="email" name="email[]">
        </td>
        <td>
            <input type="checkbox" name="first_attribute[]">
            <input type="checkbox" name="second_attribute[]">
        </td>
    </tr>
    <tr>
        <td>
            <input type="text" name="firstname[]">
        </td>
        <td>
            <input type="text" name="lastname[]">
        </td>
        <td>
            <input type="email" name="email[]">
        </td>
        <td>
            <input type="checkbox" name="first_attribute[]">
            <input type="checkbox" name="second_attribute[]">
        </td>
    </tr>
</table>

with checkboxes associated to each row. The browser will submit only checked values, you will lose the order of selected items and you will be no longer able to identify if first_attribute[0] refers to the first row (if you checked on the first row) or the second one (if you checked only on the second row and not the first).

My today's solution: use the following markup

<table>
    <tr>
        <td>
            <input type="text" name="firstname[]">
        </td>
        <td>
            <input type="text" name="lastname[]">
        </td>
        <td>
            <input type="email" name="email[]">
        </td>
        <td>
            <input type="checkbox" name="first">
            <input type="hidden" name="first_attribute[]" value="0">
            <input type="checkbox" name="second">
            <input type="hidden" name="second_attribute[]" value="0">
        </td>
    </tr>
    <tr>
        <td>
            <input type="text" name="firstname[]">
        </td>
        <td>
            <input type="text" name="lastname[]">
        </td>
        <td>
            <input type="email" name="email[]">
        </td>
        <td>
            <input type="checkbox" name="first">
            <input type="hidden" name="first_attribute[]" value="0">
            <input type="checkbox" name="second">
            <input type="hidden" name="second_attribute[]" value="0">
        </td>
    </tr>
</table>

and the following few JS lines

$('input:checkbox').change(function() {
    var name = $(this).attr('name');
    var val = $(this).prop('checked') ? '1' : '0';
    $(this).closest('td').find('input:hidden[name^=' + name + ']').val(val);
});

In this case, checkboxes are used only for user's interaction; actual and relevant values are disposed in hidden inputs, which value is set to 0 or 1 accordly to the status of the related checkbox. When the form is submitted, first_attribute[] and second_attribute[] will be entirely filled and each row will have his own 0 and 1 marked attributes.

Fuck you, legacy and inconsistant browsers' behaviours.