Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I find each table cell's "visual location" using jQuery?

I have a HTML table that contains both ROWSPANs and COLSPANs.

How can I find each cell's "visual location" using jQuery?

For example, here's a visual representation of my table with each cell populated with what the "visual location" algorithm should return:
(Note: I only really care about cells within the <tbody> and the column reference can be an integer, not an alphabetic character, I've only done this to easily highlight the problem)

+--+--+--+--+--+
|  |A |B |C |D |
+--+--+--+--+--+
|1 |A1|B1   |D1|
+--+--+--+--+  +
|2 |A2   |C2|  |
+--+     +--+  +
|3 |     |C3|  |
+--+--+--+--+--+
|4 |A4|B4|C4|D4|
+--+--+--+--+--+
|XYZ           |
+--+--+--+--+--+

I've tried implementing the first, however the reference for cell C3 is inaccurate as it doesn't take into account ROWSPANS. The second link may be able to be merged into the solution of the first, but I can't figure out how.

  • Working with tables in jQuery | My Tech World
  • What's the most efficient way to retrieve the column of rows affected by a "rowspan"? - Stack Overflow

I'm hoping to use this as a function called getCellLocation(cell) that will return an associative array that returns the location something like this:

function getCellLocation(cell)
{
  var row_number = parseInt($(cell).parent().prevAll().length) + 1;
  var col_number = 1;
  $(cell).prevAll('td').each(function() {
      col_number += $(this).attr('colspan') ? parseInt($(this).attr('colspan')) : 1;
  });

  var location = new Array();
  location['row'] = row_number;
  location['col'] = col_number;
  location['index'] = $('td').index(cell) + 1;
  return location;
}

$('table td').each(function(i){
  var cell = getCellLocation($(this));
  $(this).prepend('<span class="ref">R' + cell['row'] + ':C' + cell['col'] + ':D' + cell['index'] + '</span>');
});

Here's the HTML of the example table:

<table border="1" cellspacing="0">
  <thead>
    <tr>
      <th></th>
      <th>A</th>
      <th>B</th>
      <th>C</th>
      <th>D</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>1</th>
      <td>A1</td>
      <td colspan="2">B1</td>
      <td rowspan="3">D1</td>
    </tr>
    <tr>
      <th>2</th>
      <td rowspan="2" colspan="2">A2</td>
      <td>C2</td>
    </tr>
    <tr>
      <th>3</th>
      <td>C3</td>
    </tr>
    <tr>
      <th>4</th>
      <td>A4</td>
      <td>B4</td>
      <td>C4</td>
      <td>D4</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="5">XYZ</td>
    </tr>
  </tfoot>
</table>
<style> span { background-color: #ffc; margin-right: .5em;} </style>
like image 935
Turgs Avatar asked Jun 10 '12 06:06

Turgs


2 Answers

your solution        my proposed table design
+--+--+--+--+--+     +--+--+--+--+--+
|  |A |B |C |D |     |  |A |B |C |D |     length 5 vs 5
+--+--+--+--+--+     +--+--+--+--+--+
|1 |A1|B1   |D1|     |1 |A1|B1|//|D1|
+--+--+--+--+  +     +--+--+--+--+--+
|2 |A2   |C2|  |     |2 |A2|//|C2|//|     length 3 vs 5
+--+     +--+  +     +--+--+--+--+--+
|3 |     |C3|  |     |3 |//|//|C3|//|
+--+--+--+--+--+     +--+--+--+--+--+
|4 |A4|B4|C4|D4|     |4 |A4|B4|C4|D4|
+--+--+--+--+--+     +--+--+--+--+--+
|XYZ           |     |XY|//|//|//|//|     length 1 vs 5
+--+--+--+--+--+     +--+--+--+--+--+
// cells labeled '//' above have this class

td.stuffing { display: none; }

Do you see what I did there?

This is the third row, for example:

<tr>
  <th>2</th>
  <td rowspan="2" colspan="2">A2</td>
  <td class="stuffing"></td>
  <td>C2</td>
  <td class="stuffing"></td>
</tr>

Now the function to retrieve correct index is very simple.

function getCellLocation(cell) {
    return {row:cell.parentNode.rowIndex, cell:cell.cellIndex}
}


And here is a bonus. If you were to access a hidden cell, this will automatically traverse to the correct one, which spans over those hidden.
function getSmartCell(table, row, col) {
    var cell = table.rows[row].cells[col];
    if (cell.className.indexOf("stuffing") == -1) return cell;

    // traverse Left
    while ((row>0) && (col>0) && (cell.className.indexOf("stuffing") >= 0)) {
        cell = table.rows[row].cells[--col];
    }

    // roll back one cell if no colSpan is found
    if (cell.colSpan == 1) cell = table.rows[row].cells[++col];

    // traverse Up
    while ((row>0) && (col>0) && (cell.className.indexOf("stuffing") >= 0)) {
        cell = table.rows[--row].cells[col];
    }

    return cell;
}

Usage:

var tb = document.querySelector("table");
getCellLocation(getSmartCell(tb,3,2)); // {row: 2, cell: 1}

ATTENTION I have just checked the code of getSmartCell and it returns wrong result if there are two neighbouring rowspans. I need to fix this.

Here is an example https://jsfiddle.net/goua3m13/

like image 38
Qwerty Avatar answered Nov 15 '22 01:11

Qwerty


Here's my solution:

function getCellLocation(cell) {

    var cols = cell.closest("tr").children("td").index(cell);
    var rows = cell.closest("tbody").children("tr").index(cell.closest("tr"));
    var coltemp = cols;
    var rowtemp = rows;

    cell.prevAll("td").each(function() {
        cols += ($(this).attr("colspan")) ? parseInt($(this).attr("colspan")) - 1 : 0;
    });

    cell.parent("tr").prevAll("tr").each(function() {
        //get row index for search cells
        var rowindex = cell.closest("tbody").children("tr").index($(this));
        // assign the row to a variable for later use
        var row = $(this);
        row.children("td").each(function() {
            // fetch all cells of this row
            var colindex = row.children("td").index($(this));
            //check if this cell comes before our cell
            if (cell.offset().left > $(this).offset().left) {
                // check if it has both rowspan and colspan, because the single ones are handled before
                var colspn = parseInt($(this).attr("colspan"));
                var rowspn = parseInt($(this).attr("rowspan"));
                if (colspn && rowspn) {
                    if(rowindex + rowspn > rows)
                    cols += colspn;                    
                }
                if(rowspn && rowindex + rowspn > rows) cols +=1;
            }

        });
    });

I'm checking for cells which have both colspan and rowspan, because the rest are handled by the first five lines of this code. If the cells have both rowspan and colspan, they should be effecting other cells that are not below OR aside this cell, so I need to search for every cell's previous cells for interaction.

like image 183
Taha Paksu Avatar answered Nov 15 '22 01:11

Taha Paksu