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;
});
});
});
}());