2

I want to validate if a given html table syntax is correct, with respect to all colspan and rowspan definitions.

Example on JSFiddle

The following table is syntactically correct:

<table id="correct">
    <tr>
        <td>a</td>
        <td>b</td>
        <td rowspan="2">c</td>
    </tr>
    <tr>
        <td colspan="2">d</td>
    </tr>
</table>

The next table is wrong because both the columns and rows are not matching:

<table id="wrong1">
    <tr>
        <td>a</td>
        <td>b</td>
        <td rowspan="1">c</td>
    </tr>
    <tr>
        <td colspan="1">d</td>
    </tr>
</table>

I want to be able to validate if a table is correct or wrong. The given code is just an example, it should validate with any given table, regardless of its complexity.

I could begin to write my own validator, but before that i'd like to know if there are any libraries or already-working solutions out there. Can you help me on this?

/edit

Just found this online validator:

http://wet-boew.github.com/wet-boew/demos/tableparser/validator-htmltable.html

My first wrong table #wrong1 throws an error, but #wrong2 does not (see fiddle). Seems that it does not support too large numbers.

5
  • stackoverflow.com/questions/13503185/… It's validating the table Commented Jan 26, 2013 at 11:53
  • Check my code below, it should be working fine Commented Jan 26, 2013 at 12:13
  • @Alp Check the updated code again Commented Jan 26, 2013 at 13:29
  • @Alp What is the purpose of your table validation requirement? Is it for layout or is it to present tabular data? FYI - Here the javascript libraries of the wet-boew HTML table validator Commented Jun 1, 2013 at 2:13
  • @PierreDubois: the purpose is validating incoming hmtl documents if their syntax is correct. your link looks very interesting Commented Jun 1, 2013 at 9:50

2 Answers 2

2

There you go, here is a working code:

function validateTable(id){
    var rows = document.getElementById(id).tBodies[0].rows;
    var totalCells=0;
    // total rows and columns
    var totalRows=rows.length;;
    var totalColumns=0;;
    var foundCells=new Array();    
    var maxRows=rows.length;
    var maxColumns=0;
    var maxCellIndex = 0;
    // First we get totalRows and totalColumns
    for(var i=0;i<rows.length;i++){     
        totalColumns = Math.max(totalColumns,rows[i].cells.length);
    }

    // The matrix now should be totalRows x totalColumns
    for(var i=0;i<totalRows;i++){
        for(var j=0;j<totalColumns;j++){
            maxCellIndex = (i*totalColumns)+j;
            foundCells[ maxCellIndex ] = 0;
        }
    }

    for(var i=0;i<rows.length;i++){     
        maxColumns = Math.max(rows[i].cells.length,maxColumns);
        for(var j = 0;j<rows[i].cells.length;j++){
            var cellPosition = (i*totalColumns)+j;
            var cols=0;
            var tcells=0;
            cols = parseInt( rows[i].cells[j].rowSpan );           
            tcells = parseInt( rows[i].cells[j].colSpan );
            if(tcells>0){
                for(var k=0;k<tcells;k++){
                     foundCells[cellPosition + k] = 1; 
                }
            }
            if(cols > 0){
               for(var k=0;k<cols;k++){
                     foundCells[cellPosition + (k*totalColumns)  ] = 1;
                }
            }
           // totalCells += ( tcells * cols) ;
        }        
    }     
    // This is the updated part
    var allCellsAlignedCorrectly=true;
    for(var n=0;n<=maxCellIndex;n++){        
       if(isNaN(foundCells[n]) || parseInt(foundCells[n]) == 0){
          allCellsAlignedCorrectly = false;
       }
    }
    for(var n=0;n<=foundCells.length;n++){
        if(!isNaN(foundCells[n])){
           totalCells+=foundCells[n];
        }
    }
    // alert(foundCells);
    // alert(totalCells+":"+totalColumns+":"+totalRows);
    return (((totalCells) == (maxRows*maxColumns)) && allCellsAlignedCorrectly);
}

Updated again Check again

you can see it live here:

http://jsfiddle.net/UkV35/8/

Sign up to request clarification or add additional context in comments.

7 Comments

if your table have many tBodies ( you can loop through them too )
@Alohci check the code again, now it uses a matrix array to validate (It's not optimized but it works)
There is still a bug: jsfiddle.net/UkV35/7 - #correct2 is validated as wrong
still doesn't work, please check #correct2 from this fiddle: jsfiddle.net/UkV35/7
Well the code would need to shift the X-Cells in the next row that have a cells with colspan > 0, I can think about something, but I don't have much time, you can continue from here.
|
1

Based on the answer of Shehabix, i rewrote the validator. Main improvements:

  • Correctly computes all colspan and rowspan combinations (hopefully)
  • Detects overlaps, cells outside the table dimensions and missing cells
  • Outputs specific error messages

DEMO

(see the developer console for output)

JavaScript

function validateTable(id){
    console.log('[validating table #' + id + ']');
    var rows = document.getElementById(id).tBodies[0].rows;
    var hasErrors = false;

    // total rows and columns
    var totalRows = rows.length;
    var totalColumns= 0;
    for(var row=0; row<rows.length; row++) {
        var cells = rows[row].cells;
        var cols = 0;
        for(var col=0; col<cells.length; col++) {
            var cell = rows[row].cells[col];
            var colspan = parseInt(cell.colSpan);
            if(colspan > 1) {
                cols += colspan;
            } else {
                cols++;
            }
        }
        totalColumns = Math.max(totalColumns, cols);
    }

    var cells = {};
    cells.init = function(row, col, options) {
        cells[row + ':' + col] = $.extend({
            row: row,
            col: col,
            count: 0
        }, options);
    }
    cells.update = function(row, col, options) {
        var cell = cells[row + ':' + col];
        if(!cell) {
            hasErrors = true;
            console.log('cell outside of table dimensions (cell ' + (row+1) + ':' + (col+1) + ' is outside of allowed table size ' + totalRows + ':' + totalColumns + ')');
            return;
        }
        cells[row + ':' + col].count++;
        if(options) {
            cells[row + ':' + col] = $.extend(cells[row + ':' + col], options);
        }
    }
    cells.get = function(row, col) {
        return cells[row + ':' + col];
    }

    var colspans = {};
    colspans.add = function(row, col, count) {
        for(var coladd=0; coladd<count; coladd++) {
            colspans[row + ':' + (col+coladd)] = true;
        }
    };
    colspans.check = function(row, col) {
        return colspans[row + ':' + col];
    };

    var rowspans = {};
    rowspans.add = function(row, col, count) {
        for(var rowadd=0; rowadd<count; rowadd++) {
            rowspans[(row+rowadd) + ':' + col] = true;
        }
    };
    rowspans.check = function(row, col) {
        return rowspans[row + ':' + col];
    };

    // init cell matrix
    for(var row=0; row<totalRows; row++) {
        for(var col=0; col<totalColumns; col++) {
            cells.init(row, col);
        }
    }

    for(var row=0; row<rows.length; row++) {
        var colskip = 0;
        var rowskip = 0;
        for(var col=0; col<totalColumns; col++) {
            // check if this cell is pushed by a colspan
            if(colspans.check(row, col)) continue;

            // check if this cell is pushed by a rowspan
            if(rowspans.check(row, col)) {
                rowskip++;
                continue;
            }

            console.log("row: " + row + " - col: " + (col-colskip-rowskip));
            var cell = rows[row].cells[col-colskip-rowskip];
            if(!cell) continue;

            var rowspan = parseInt(cell.rowSpan);
            var colspan = parseInt(cell.colSpan);

            cells.update(row, col, {
                element: cell
            });
            if(colspan > 1){
                colskip += colspan-1;
                colspans.add(row, col+1, colspan-1);
                for(var coladd=1; coladd<colspan; coladd++) {
                    cells.update(row, col+coladd, {
                                 element: cell
                                 });
                }
            }
            if(rowspan > 1){
                rowspans.add(row+1, col, rowspan-1);
                for(var rowadd=1; rowadd<rowspan; rowadd++) {
                    cells.update(row+rowadd, col, {
                                 element: cell
                                 });
                }
            }
        }
    }

    for(var row=0; row<totalRows; row++) {
        for(var col=0; col<totalColumns; col++) {
            var cell = cells.get(row, col);
            if(cell.count == 1) {
                // everything is fine
            } else if(cell.count == 0) {
                hasErrors = true;
                console.log("cell " + (row+1) + ':' + (col+1) + " is missing");
            } else {
                hasErrors = true;
                console.log("cell " + (row+1) + ':' + (col+1) + " is overlapping with rowspan (cell usage count of " + cell.count + ")");
            }
        }
    }

    console.log('table is ' + (hasErrors ? 'invalid' : 'valid'));
    return hasErrors;
}

--

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.