Question

This may take a bit of explaining, so please bear with me.

I'm trying to create X number of tables, each of a varying length (columns) Y, with 4 rows each, X and Y being determined by the user. This part I have solved.

The problem is that every cell in every table must contain it's own text box, which will contain numbers derived from a database. If the user changes the number in the text box, the change must then be reflected in the database.

Given that the number of tables and the length of the tables are unknown, I can't set up the names of the text boxes ahead of time and must do so dynamically.

I ran across this code:

var pageNumber = 1;
eval("var text" + pageNumber + "=123;");
alert(text1);
//alert shows '123'

Which (with modification), I believe would allow me to dynamically create a unique name for each text box. What I am having trouble with is how do I then access this new text box since I don't know what it's called(after the table has been created)?

I plan on using a 3d array to hold the numbers that I want displayed in each cell in the table and plan to use this array to name the cell.

As an example the array could be array[i][j][k], and lets say i=1, j=2, k=3, which would hold the number 8. The dynamically created text box name would be something like: textBox123

How do I associate that cell in the array (containing 8) with that new text box and then retrieve and store a new value when I don't know it's name ahead of time?

The way I'm thinking of it, how do I write something like

 <input type="text" name="textBox123" value=array[i][j][k]> 

to the html?

Then again, maybe I'm over thinking this and there is a better way of doing it. If so, please let me know.

Was it helpful?

Solution

HTML:

<div id="tables"></div>

JavaScript:

var tables = [
        // Table #1
        [
            [111, 112, 113], // Row #1
            [121, 122, 123]  // Row #2
        ],
        // Table #2
        [
            [211, 212, 213, 214], // Row #1
            [221, 222, 223, 224], // Row #2
            [231, 232, 233, 234]  // Row #3
        ],
        // Table #3 (empty!)
        []
    ],
    nr, nc, i, j, k,
    name, value, textBox, cell, row, table,
    $tables = $("#tables");

for (i = 0; i < tables.length; ++i) {
    nr = tables[i].length;
    nc = nr > 0 ? tables[i][0].length : 0;
    table = "<table>";
    for (j = 0; j < nr; ++j) {
        row = "<tr>";
        for (k = 0; k < nc; ++k) {
            name = "textBox" + (i + 1) + (j + 1) + (k + 1);
            value = tables[i][j][k];
            textBox = "<input type='text' name='" + name + "' value='" + value + "'>";
            cell = "<td>" + textBox + "</td>";
            row += cell;
        }
        row += "</tr>\n";
        table += row;
    }
    table += "</table>\n";
    $tables.append(table);
}

See the jsfiddle. The 3rd, empty table will be inserted as an empty table tag, but this is not necessary.

I would like to mention that since you need to connect these tables to a database, an elegant solution would be using a client-side MVC framework, like Backbone.js. Here you find a good intro book. The View could use a code similar to the one above to render the tables. The tables array could be stored in a Collection.

OTHER TIPS

You don't have to pre-tag every cell in the table with its index because you can calculate it based on the DOM hierarchy upon demand.

If you have an arbitrary cell in a table, you can get the row and column number of that cell in the table like this where cell as passed to this function can either be the td object or any descendant of it (such as your <input> tag that's in the table):

// Pass any td object or descendant of a td object in a <table> and it will return
// an object with {row: x, col: y}
// or return null if the cell passed wasn't inside a <tr> and <td>
function findRowColInTable(cell) {

    function findParent(obj, parentTagName) {
        parentTagName = parentTagName.toUpperCase();
        while (obj && obj.tagName != parentTagName) {
            obj = obj.parentNode;
        }
        return obj;
    }

    findChildCnt(obj) {
        var cnt = 0;
        while (obj = obj.prevSibling) {
            ++cnt;
        }
        return cnt;
    }

    var td, tr, col, row;

    // get table cell
    td = findParent(cell, "TD");
    if (td) {
        col = findChildCnt(td);
        tr = findParent(td, "TR");
        if (tr) {
            row = findChildCnt(tr);
            return {row: row, col: col};
        }
    }
    return null;
}

FYI, this is quite a bit easier in jQuery where you can use .closest(tag) and .index() to find appropriate parents and get your local sibling count, but I've offered you a pure javascript version.

You can actually do arrays on forms. Take my old answer for example, you can create arrays in form inputs with such naming scheme. On the back-end, you get them as arrays (Assuming PHP).

If you are grabbing the values via JS instead, you can base it off this answer, which takes advantage of the naming scheme, grabs the form values and turns them into JS objects and arrays similar to how PHP would receive them on the back-end. Basically it runs through the form and gets the form values. It's partly jQuery, but you can get the source from jQuery if you don't want to incorporate the entire library.

You can make use of javascript object oriented concept

Each table has its corresponding table object, so does its rows and cells. With this technique, every object has its "element" in the real DOM.

The trick is, attach the event listener (e.g. when user types something in the textbox) to the object, so that that event listener will only correspond to that specific cell, not messing up with other cells in other column, row, and tables.

Here is possible solution. Sorry for my jQuery heavy dependent. I am just not sure how to create element with document.createElement() (my bad knowledge), you can of course change them with pure javascript implementation (no jQuery).

     values=new Array();
     function Cell(id,values_row){ //Cell class
        this.id=id;
        this.element=$("<td></td>");           
        this.textbox=$("<input type='text' name='"+this.id+"' id='"+this.id+"'></input>");
        values_row[id]="" //initiate default value == ""
            //here is the trick
        this.textbox.change(function(){ //you can use onchange if you dont like jQuery
            values_row[id]=$(this).val();             
            //other tasks need to be done when user changes the value of a textbox (typing)
        });
        this.textbox.appendTo(this.element);    
    }
    function Row(id,number_of_columns,values_table){ //Row class
        this.id=id
        this.element=$("<tr></tr>")                
        values_table[id]=new Array(); //make array "values" becomes 3D              
        for(var col =0;col<number_of_columns;col++){
            var the_cell=new Cell(col,values_table[col]);
            the_cell.element.appendTo(this.element);
        }
    }
    function Table(id,number_of_columns){ //Table class
        this.id=id
        this.element=$("<table></table>")
        values[id]=new Array();  //make array "values" becomes 2D                     
        for(var row=0;row<4;row++){
            var the_row=new Row(row,number_of_columns,values[id]);
            the_row.element.appendTo(this.element)                       
        }
    }
    // creating the tables
    for(var t=0;t<number_of_tables_needed;t++){
        the_table=new Table(t,number_of_column_for_this_table);
        the_table.element.appendTo(table_container_element);
    }
    //do input some text into some cells then
    alert(values)

The alert will display a 3D array containing the values of all cells indexed by table number, row number, and of course the column that points exactly to a cell. The default value for cell's value is ""

With this technique, you do not need find which the correct array element for a corresponding cell whose value just been inputted. Instead, attach the event listener prior to the element creation in DOM.This is an object oriented way / approach to the problem.

Rather than having O(n^3) algorithm complexity when you need to find the corresponding array cell each time user makes an input, this solution is a taking a bit more time during object initialization, but afterwards, it is O(1).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top