Merge similar cells in tables

by Scott Kaye

HTML

<table border="1">
    <tr>
        <th>0</th>
        <th>A</th>
        <th>B</th>
        <th>C</th>
        <th>D</th>
        <th>E</th>
        <th>F</th>
    </tr>
    <tr>
        <td colspan="6">Test</td>
        <td>A</td>
    </tr>
    <tr>
        <td>Test</td>
        <td no-merge>Test</td>
        <td>Test</td>
        <td>Test</td>
        <td>Test</td>
        <td>Test</td>
        <td>B</td>
    </tr>
    <tr>
        <td>1</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td colspan="3" no-merge>B</td>
    </tr>
    <tr>
        <td>2</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>3</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>4</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>5</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>6</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>7</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>8</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>9</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
    <tr>
        <td>10</td>
        <td>A</td>
        <td>A</td>
        <td>A</td>
        <td>B</td>
        <td>B</td>
        <td>B</td>
    </tr>
</table>

CSS

td {
    padding: 10px;
}

th {
    width: 30px;
}

table {
    table-layout: fixed;
}

[no-merge] {
    background-color: #ffc;
}

JavaScript

(function TableJoin() {
    const maxX = 3;
    const maxY = 3;
    let tables = document.querySelectorAll("table");

    tables.forEach(table => {
        // Join columns
        table.querySelectorAll("tr").forEach(tr => {
            let colFirst = null;
            tr.querySelectorAll("td").forEach(td => {
				if (td.hasAttribute("no-merge")) {
					colFirst = null;
					return;
				}
			
                if (colFirst && td.innerHTML == colFirst.innerHTML && colFirst.colSpan < maxX) {
                    colFirst.colSpan += td.colSpan;
                    td.parentNode.removeChild(td);
                } else {
                    colFirst = td;
                }
            });
        });

        // Join rows
        let rowFirst = [];
        table.querySelectorAll("tr").forEach(tr => {
            let xpos = 0;
            tr.querySelectorAll("td").forEach(td => {
				if (td.hasAttribute("no-merge")) {
					delete rowFirst[xpos];
					return;
				}
			
                if (rowFirst[xpos] && td.innerHTML == rowFirst[xpos].innerHTML && td.colSpan == rowFirst[xpos].colSpan && rowFirst[xpos].rowSpan < maxY) {
                    rowFirst[xpos].rowSpan += td.rowSpan;
                    td.parentNode.removeChild(td);
                } else {
                    // Delete row merge cells that this cell spans
                    for (let i = xpos; i < xpos + td.colSpan; ++i) {
                        delete rowFirst[i];
                    }

                    rowFirst[xpos] = td;
                }

                xpos += td.colSpan;
            });
        });
    });
}());