Merging 2 (selected) TD in Pure Javascript

Hello all,

I’ve searched the internet but could not find a solution in pure JS for this.
I’d like to merge 2 or multiple (selected) TD’s but can’t get it done.

In HTML we use col- and row-span, but how can I do it in JS?

You can edit the colspan or rowspan attribute from JavaScript too.

var td = document.querySelector('td');
td.setAttribute('colspan', 2);

How do you intend for the cells to be selected?

Hello Paul.
Each TD contains a DIV. The user can select them and they will turn red.
Upon pressing a button the 2 selected DIV will be hidden and the underlying TDs should be merged

With that sort of control scheme, some desired layouts don’t seem to be technically possible. How do you plan to deal with such conflicts?

Meanwhile, I’ll put this merge identical table cells on the back-burner :slight_smile:

I am still learning and trying to see what is possible and what isn’t.
As for the code you gave in your previous code, it does not merge but “adds an column”.
Is there no way to “merge them” or should I delete one and use colspan for the other?

You need to extract the content from the one to be deleted first and add that into the end of the one you add the colspan to. Then they will be effectively merged.

Thats what I thought.
Thanks felgall!

This task looks very interesting.
I made a little attempt to implement it: http://jsfiddle.net/9ppgu386/1/
You can use that code as reference if you want

2 Likes

Nice work megazoid:)

Nice work indeed!

Thanks guys! There is a lot of room for improvements, though.

A slightly different approach using only one button instead of two:

document.getElementById('mergeBtn').onclick = merge;

function onCellClick(e){
var targ = (window.event) ? window.event.srcElement : e.target;
if (3===targ.nodeType) {
  targ = targ.parentNode;
}
    var isSelected = targ.className.indexOf('selected') >= 0;
        
    if (!isSelected) targ.className =  'selected';    
    else targ.className = '';        
    isSelected = !isSelected;        
}

document.getElementById('table').addEventListener('click', onCellClick, false );

function merge() {
var rows = document.getElementById('table').rows;
    for (var i = 0, ii = rows.length; i < ii; i++) {
      for (var j = 0, jj = rows[i].cells.length; j < jj; j++) {
       if (rows[i].cells[j].className.indexOf('selected') >= 0) {
         if (rows[i].cells[j+1].className.indexOf('selected') >= 0) mergeRow(i,j);
      } else if (rows[i+1].cells[j].className.indexOf('selected') >= 0) mergeCol(i,j);
         }
    }
}

function mergeRow(i,j) {
  var r = document.getElementById('table').rows[i];
  var c = r.cells[j+1].innerHTML;
 r.cells[j].innerHTML += c;
 var n = r.cells[j].getAttribute('colspan') || 1;
 r.cells[j].setAttribute('colspan',n+=1);
 r.deleteCell(j+1);
 r.cells[j].className = '';
  
}
function mergeCol(i,j) {
  var t = document.getElementById('table');
  var c = t.rows[i+1].cells[j].innerHTML;
 t.rows[i].cells[j].innerHTML += c;
 var n = r.cells[j].getAttribute('rowspan') || 1;
 t.rows[i].cells[j].setAttribute('rowspan', n+=1);
 t.rows[i+1].deleteCell(j);
 t.rows[i].cells[j].className = '';
}

same CSS and mostly the same HTML - also lots of room for improvement

1 Like

felgall, would you mind showing the HTML? The behavior seems odd. Sorry, I can’t figure it out.

One of the } appears to have been misplaced (not quite sure when that happened, it was in the right place while I was testing)

if (rows[i].cells[j].className.indexOf('selected') >= 0) {
        if (rows[i].cells[j+1].className.indexOf('selected') >= 0) mergeRow(i,j);
      else if (rows[i+1].cells[j].className.indexOf('selected') >= 0) mergeCol(i,j);
   } 

HTML would be

<table id="table">
    <tbody>
        <tr>
            <td>A</td>
            <td>B</td>
            <td>C</td>
            <td>D</td>
        </tr>
        <tr>            
            <td>E</td>
            <td>F</td>
            <td>G</td>
            <td>H</td>
        </tr>        
        <tr>
            <td>I</td>
            <td>J</td>
            <td>K</td>
            <td>L</td>
        </tr>        
    </tbody>
</table>

<button id="mergeBtn">Merge</button>

Note that the validation is still incomplete and it is possible to select combinations to merge that will destroy the table.

A better solution would be possible if it were possible to determine the row and cell position of the first cell with the class set without having to examine every cell looking for it.

I have done some more work on this and got rid of the buttons completely. It now merges cells by clicking them - at least it does most of the time - there are still a few combinations where it doesn’t merge or merges incorrectly but it is a lot closer than the above version.

1 Like

Slightly modified version that works better when merging cells that both already have colspan or rowspan

Note that each of the versions I have provided will only work properly for multiple merges if you start merging from the bottom right of the table. If cells have been merged above or to the left of the ones to be merged then the cell counts for working out which are adjacent will no longer match up. For example if you merge IJ then the code will not let you merge GK as it doesn’t recognise that they are in the same column. It would however allow you to merge FK as it mistakenly identifies those as adjacent and so makes a mess of the table.

To get the code to work properly for all possible merges we need to be able to accurately identify which cells are adjacent even when other cells have already been merged.

This is an interesting problem which requires some serious thinking to solve it, as you have already found out.
I have been fiddling with it for some time and have finally worked out how to do it. The secret is to build a “map” of the original table cell structure so that you don’t lose the row / column relationship as you merge cells. The map is just an array of arrays of the form

var B=[ [1,1,1,1,1], [1,1,1,1,1], [1,1,1,1,1],[1,1,1,1,1] ];

The program uses this structure to decide whether two cells are adjacent and modifies the numbers depending on the merge being two row cells or two column cells. After the first cell is selected, adjacent cells capable of being merged are highlighted to assist in identifying them.

My program could be improved by maintaining a fixed cell width and height, rather than the percentage values I have used in the example. I could also merge the text within the merged cells, rather than delete the text from the deleted cell, but these are all straight forward changes and do not change the method used to merge the cells.

The example is on jsFiddle at http://jsfiddle.net/AllanP/w7u27s4s/ for those interested in playing with it. :grinning:

3 Likes

Another possible improvement would be to dynamically build the original array by examining the table which also shouldn’t be too difficult to add to your current code.

I am looking forward to examining your code in detail to see just how you managed to keep track of the adjacent cells…

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.