Hopefully my last question for a little while, although I can't be sure...
I'm trying to produce a form where once data is input it creates a new area for inputting data. This seems like a very trivial problem, and I can think of several possible uses for such a form (inputting job history on an application, inputting a list of phone numbers where you can be reached, etc.)
It's easy enough to create a new row once data is entered, however I can't find a way to tell this new row that when data is entered into it, it should create yet another row.
For simplicity, and to help get the appearance I wanted, I was using a table for my form and each time anything was typed into the last row it would create a new row. Here's the HTML that I'm using:
<table>
<tr>
<th>Field 1</th>
<th>Field 2</th>
<th>Field 3</th>
</tr>
<tr>
<input type="hidden" name="id-row-0" value="0" />
<td><input type="text" name="field1-row-0" value="" /></td>
<td><input type="text" name="field2-row-0" value="" class="date-field" /></td>
<td><input type="text" name="field3-row-0" value="" /></td>
</tr>
</table>
Then I have the following javascript:
$.spreadsheet = {
fields : $('#myForm').find('input,textarea,select'), // list of input HTML elements
rows : $('#myForm').find('tr').length - 1, // number of rows currently. Subtract 1 for table headers
columns : $('#myForm').find('tr')[0].cells.length // number of fields pulled from the database
}
$($.spreadsheet.fields).find('input,textarea,select').change( function() {
var index = $.spreadsheet.fields.index( $( this ) );
var row = Math.floor(index / $.spreadsheet.columns);
if( row == $.spreadsheet.rows - 1 ) {
// Last row was changed. Do we add a new row?
if( $(this).val() != '' ) {
// A value was added. We ned a new row
new_row = $("input[name=id-row-"+row+"]").parent().clone().appendTo("#myForm table");
// Now we need to change the name of every element in this row to reflect the new row number
new_row.find('input,textarea,select').each( function() {
partial_name = this.name.substring(0, this.name.indexOf('-row-'+row));
this.name = partial_name + '-row-' + (row+1);
// If it was a datepicker before then the id will cause all sorts of issues
this.id = '';
$(this).removeClass('hasDatepicker');
});
// Create date pickers
new_row.find('.date-field').datepicker({ dateFormat: "yy-mm-dd", changeMonth: true, changeYear: true });
// Recursively set this function for change()?
// Help, guys =(
// Update the total number of rows
$.spreadsheet.rows += 1;
}
}
});
// Create date pickers
$( ".date-field" ).datepicker({ dateFormat: "yy-mm-dd", changeMonth: true, changeYear: true });
As you can see, I'm not sure how to recursively set this function as the callback for .change()
. I tried doing something like the following:
function add_new_row = function() {
...
// Recursively set this function for change()?
new_row.find('input,textarea,select').change( add_new_row );
...
}
$($.spreadsheet.fields).find('input,textarea,select').change( add_new_row );
But it didn't work. Does javascript not support any form of recursion? And if not, how does one solve this dilemma?
After using .delegate()
at the suggestion of @J0HN, here is my current javascript file in its entirety:
$(document).ready( function() {
$.spreadsheet = {
fields : $('#myForm').find('input,textarea,select'), // list of input HTML elements
rows : $('#myForm').find('tr').length - 1, // number of rows currently. Subtract 1 for table headers
columns : $('#myForm').find('tr')[0].cells.length, // number of fields pulled from the database
}
// Add new rows
$("#myForm").delegate("input, textarea, select", "change", function() {
var index = $.spreadsheet.fields.index( $( this ) );
var row = Math.floor(index / $.spreadsheet.columns);
var col = index % $.spreadsheet.columns;
if( row == $.spreadsheet.rows - 1 ) {
// Last row was changed. Do we add a new row?
if( $(this).val() != '' ) {
// A value was added. We ned a new row
// This row should look just like the previous... A CLONE, one might say?
new_row = $("input[name=id-row-"+row+"]").parent().clone().appendTo("#myForm table");
// Now we need to change the name of every element in this row to reflect the new row number
new_row.find('input, textarea, select').each( function() {
partial_name = this.name.substring(0, this.name.indexOf('-row-'+row));
this.name = partial_name + '-row-' + (row+1);
// We can't initialize a datepicker if it thinks one is already initialized
$(this).removeClass('hasDatepicker');
this.id = '';
});
// Create date pickers
new_row.find('.date-field').datepicker({ dateFormat: "yy-mm-dd", changeMonth: true, changeYear: true });
// Update the total number of rows
$.spreadsheet.rows += 1;
}
}
});
// Create date pickers
$( ".date-field" ).datepicker({ dateFormat: "yy-mm-dd", changeMonth: true, changeYear: true });
});
When I type some data into the first row it successfully adds a new row, as expected. Using Chrome's "Inspect Element" feature I can check the row numbers on these new elements and they all seem correct, and the date pickers even work properly.
However when I type data into this new row nothing happens. No rows are added, there are no errors, javascript doesn't crash (datepickers continue to work)... It's as if the "change" method is never called.
Ideas?