How to highlight cells/rows/columns in one table as matching cells/rows/columns in another table are highlighted - cont'd

Hi to all,

Please see previous post:

Firstly, thank you again to all who helped thus far - I have more questions to ask, if that’s okay…

I hope this doesn’t seem too weird but I would like the tables to behave as follows (while retaining the existing functions): Please see the linked html/css files (note: the css file ref is commented out)

Table 2: The top row is currently a row header.

I would like it if I could hover over the header row’s cells and doing so causes the related column to highlight (including the header cell) as the header cells are hovered over.

And, clicking a header cell causes the column below to become highlighted (selected) until clicked again.

Also, it would be great if Table 2 could be horizontally scrollable, but as the table scrolls L/R the header row and the highlighting of any selected column(s) doesn’t scroll; e.g. if columns 4, 6 and 7 are selected (by clicking those header cells), then as you scroll L/R, those columns remain selected (highlighted) as the table’s data scrolls L/R (i.e. columns 4, 6 and 7 remain highlighted, until those header cells are clicked again).

Sorry if that’s a lot to ask. I am trying to make a sort of educational puzzle. I have tried to figure this out myself but am getting nowhere.

If any of it can be done using CSS that would be interesting (Please see css example link).

Any help would be greatly appreciated. Thank you very much for your time.

All the best,

77713

index-1.html (4.7 KB) screen-1.css (1.1 KB)

scrolling css example:
index-scroll.css (822 Bytes) index-scroll.html (3.0 KB)

Sorry if I didn’t word it well… perhaps put more simply:
I would like the tables to behave as follows (while retaining the existing functions)
Please see the html/css files (note: the css file reference in the html is currently commented out)

Table 2: The top row is a row header.

1 - I would like it if I could hover over that header row’s cells and doing so causes the column below to highlight (including the header cell).

2 - Also, clicking a header cell causes the column below to remain highlighted (i.e. selected) until clicked again.

3 - Also, if Table 2 data could be horizontally scrollable, and as the table’s data scrolls L/R, the header row’s data plus the highlighting of any selected column(s) and header cell(s) doesn’t scroll; e.g. if columns 4, 6 and 7 are selected (by clicking those header cells), then as you scroll L/R, those specific columns remain selected as the table’s data scrolls (i.e. the columns 4, 6 and 7 remain highlighted until those header cells are clicked again and become un-highlighted). Perhaps put more simply: only the table’s data scrolls.

I have tried to figure this out in javascript but am getting nowhere.

If any of it can be done using CSS that could also be useful (Please see horizontally-scrolling css example).
Many thanks
77713

I’m not sure if understand item 3. When only the table-body scrolls, part of the header will always be hidden. Or do you want 9 header cells which are visible and add more cells to rows of the table-body?

Is this the scroll effect you’re trying to achieve? Or am I far off?


<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">

<title>table stuff</title>

<style media="screen">
table {
 border-collapse: collapse;
 width: 800px;
}
td, th {
 padding: 0.4em;
 border: 1px solid #999;
 width: 11.11%;
}
input {
 width: 800px;
}
td:nth-of-type(3), th:nth-of-type(3) {
 background: #9ce;
}
</style>
</head>

<body>

<table id="table1">
 <thead><tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr></thead>
 <tbody><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></tbody>
</table>

<input id="slider" type="range" value="0" min="0" max="17" step="1">

<script>

var tbl = document.getElementById('table1');
var inp = document.getElementById('slider');

var cells = Array.from(Array(26)).map((_, i) => (10+i).toString(36));

function populate(idx) {
 var idx = +inp.value;
 for(var i=0; i<9; i++) {
  tbl.tBodies[0].rows[0].cells[i].innerText = cells[i + idx];
 }
};
populate();

inp.addEventListener('input', populate);
inp.addEventListener('wheel', wheel);
tbl.addEventListener('wheel', wheel);

function wheel(e) {
 inp.value = +inp.value + (e.deltaY > 0 ? 1 : -1);
 populate();
};

</script>

</body>
<html>```
1 Like

Hi alperquin and thank you for your kind reply; You’ve got it - excellent once again - and super-fast too, wow!
(If we could please have existing functions intact and the ability to choose the actual cell-data I’d be well on my way…).
My apologies if it’s a bit confusing to explain.
Thank you again alperquin, you’re brilliant!
All the best.
77713

I combined everything together. Is this working as it should?


<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">

<title>table stuff</title>

<style media="screen">

table {
 border-collapse: collapse;
 table-layout: fixed;
 width: 800px;
}

#slider {
 width: 800px;
}

td, th {
 padding: 0.4em;
 border: 1px solid #999;
}

.highlight, .highlight.selectedCol {
    background-color: #fc9; 
}

.selected, .selected.selectedCol {
    background-color: #9ce; 
}

.selectedCol {
    background-color: rgba(153,	204, 238, .5);
    background-color: #99ccee77;
}

td.hidden {
 display: none;
}

</style>

</head>
<body> 

<h2>Table One</h2>

 <table id="table1">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>
   <tr><td>9</td><td>k</td><td>N</td><td>F</td><td>p</td><td>R</td><td>u</td><td>b</td><td>S</td></tr>
   <tr><td>6</td><td>Y</td><td>L</td><td>v</td><td>X</td><td>M</td><td>c</td><td>a</td><td>j</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>W</td><td>B</td><td>w</td><td>P</td><td>q</td><td>T</td></tr>
   <tr><td>0</td><td>u</td><td>b</td><td>E</td><td>O</td><td>X</td><td>l</td><td>Z</td><td>p</td></tr>
   <tr><td>3</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td></tr>
  </tbody>
 </table>

<h2>Table Two</h2>

 <table id="table2">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>
   <tr><td>9</td><td>k</td><td>R</td><td>u</td><td>N</td><td>F</td><td>p</td><td>b</td><td>S</td><td>N</td><td>F</td><td>S</td></tr>
   <tr><td>8</td><td>Y</td><td>L</td><td>c</td><td>a</td><td>j</td><td>X</td><td>X</td><td>M</td><td>Y</td><td>X</td><td>X</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>q</td><td>T</td><td>W</td><td>B</td><td>w</td><td>P</td><td>B</td><td>H</td><td>W</td></tr>
   <tr><td>0</td><td>b</td><td>E</td><td>p</td><td>X</td><td>u</td><td>l</td><td>Z</td><td>p</td><td>E</td><td>Z</td><td>E</td></tr>
   <tr><td>7</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td><td>K</td><td>V</td><td>K</td></tr>
 </table>

<input id="slider" type="range" value="0" min="0" step="1"> <!-- correct max for amount of columns set by script -->

<script>

(function() {
 "use strict";

 var onlyPairs = true,
     t1 = document.getElementById('table1'),
     t2 = document.getElementById('table2'),
     inp = document.getElementById('slider');

 var arr1 = ArrayFromTable(t1),
     arr2 = ArrayFromTable(t2),
     t1cols = arr1[0].length,
     t2cols = arr2[0].length;

 t1.addEventListener('mouseover', highlight.bind(t1, t2, arr1, arr2, true));
 t2.addEventListener('mouseover', highlight.bind(t2, t1, arr2, arr1, true));
 t1.addEventListener('mouseout', highlight.bind(t1, t2, arr1, arr2, false));
 t2.addEventListener('mouseout', highlight.bind(t2, t1, arr2, arr1, false));
 t1.addEventListener('click', select);
 t2.addEventListener('click', select);

 inp.addEventListener('input', scrollCells);
 inp.addEventListener('wheel', wheel);
 t2.addEventListener('wheel', wheel);

 inp.max = t2cols - t1cols;
 scrollCells();

 function ArrayFromTable(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).map(function(cell) {
    return cell.textContent;
   });
  });
 };

 function scrollCells() {
  var index = +inp.value;
  for(var i=1; i<=t2cols; i++) {
    addClassToColumn('hidden', t2, i, i, (i <= index || i > index + t1cols));
    addClassToColumn('highlight', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'highlight'));
    addClassToColumn('selectedCol', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'selectedCol'));
  }
 };

 function wheel(e) {
  inp.value = +inp.value + (e.deltaY > 0 ? 1 : -1);
  scrollCells();
 };

 function toggleClass(node) {
  node.classList.toggle(this.class, this.mode);
 };

 function hasClass(node, className) {
  return node != null && node.classList.contains(className);
 };

 function addClassToColumn(className, tbl, indexTH, indexTD, mode) {
  tbl.querySelectorAll(`th:nth-of-type(${indexTH}), td:nth-of-type(${indexTD})`).forEach(function(node){
   node.classList.toggle(className, mode);
  });
 };

 var highlightColumn = addClassToColumn.bind(null, 'highlight'),
     selectColumn = addClassToColumn.bind(null, 'selectedCol');

 function highlight(tbl, arrA, arrB, mode, event) {
  if(event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   highlightColumn(t1, index, index, mode);
   highlightColumn(t2, index, index + +inp.value, mode);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      char = event.target.textContent;

  function highlightTableCells(tbl, arr, char) {
   for(var c=0; c<arr[r].length; c++) {
    if(arr[r][c] == char) {
     tbl.tBodies[0].rows[r].cells[c].classList.toggle('highlight', mode);
    }
   }
  };

  highlightTableCells(this, arrA, char);
  highlightTableCells(tbl, arrB, char);
 };

 function select(event) {
  if(event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   selectColumn(t1, index, index);
   selectColumn(t2, index, index + +inp.value);
  }
  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      a = t1.tBodies[0].rows[r].querySelectorAll('.selected'),
      A = t1.tBodies[0].rows[r].querySelectorAll('.highlight'),
      b = t2.tBodies[0].rows[r].querySelectorAll('.selected'),
      B = t2.tBodies[0].rows[r].querySelectorAll('.highlight');

  a.forEach(toggleClass, {class: 'selected', mode: false});
  b.forEach(toggleClass, {class: 'selected', mode: false});

  if((!onlyPairs || A.length && B.length) && !(hasClass(a[0], 'highlight') || hasClass(b[0], 'highlight'))) {
   A.forEach(toggleClass, {class: 'selected', mode: true});
   B.forEach(toggleClass, {class: 'selected', mode: true});
  }
 };

}());

</script>
</body>
</html>```
1 Like

Yes, excellent - it’s so very close to how I’d like it… there are just a couple of minor things really:

  1. Make the header row on Table 1 static (i.e. header data remains as is - and we add no highlighting); or we just remove that header.
  2. Don’t highlight Table 1 columns at all (when Table 2 header cells are hovered /clicked).
  3. Reverse the scrolling direction of Table 2 (moving slider Right causes data to move Right)

All looks fab otherwise! Wonderful stuff indeed alperquin.

Many thanks yet again - you’ve really brightened my rainy weekend, you lovely person!
All the best to you.
Cheers!
77713

…one other thing that would make it even better would be:

If all the cells in the first column of Table 1 (currently Column 0) could be already selected (when /if there are matches) when opening the html page or refreshing it?

Many thanks.

Good to hear it’s almost as wished. I updated the code to fix the minor imperfections. I fixed the slider by turning it 180 degrees around, so I hope it’s working correctly.


<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">

<title>table stuff</title>

<style media="screen">

table {
 border-collapse: collapse;
 table-layout: fixed;
 width: 800px;
}

#slider {
 width: 800px;
 transform: rotate(180deg);
}

td, th {
 padding: 0.4em;
 border: 1px solid #999;
}

.highlight, .highlight.selectedCol {
    background-color: #fc9; 
}

.selected, .selected.selectedCol {
    background-color: #9ce; 
}

.selectedCol {
    background-color: rgba(153,	204, 238, .5);
    background-color: #99ccee77;
}

td.hidden {
 display: none;
}

</style>

</head>
<body> 

<h2>Table One</h2>

 <table id="table1">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>
   <tr><td>9</td><td>k</td><td>N</td><td>F</td><td>p</td><td>R</td><td>u</td><td>b</td><td>S</td></tr>
   <tr><td>6</td><td>Y</td><td>L</td><td>v</td><td>X</td><td>M</td><td>c</td><td>a</td><td>j</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>W</td><td>B</td><td>w</td><td>P</td><td>q</td><td>T</td></tr>
   <tr><td>0</td><td>u</td><td>b</td><td>E</td><td>O</td><td>X</td><td>l</td><td>Z</td><td>p</td></tr>
   <tr><td>3</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td></tr>
  </tbody>
 </table>

<h2>Table Two</h2>

 <table id="table2">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>
   <tr><td>9</td><td>k</td><td>R</td><td>u</td><td>N</td><td>F</td><td>p</td><td>b</td><td>S</td><td>N</td><td>F</td><td>S</td></tr>
   <tr><td>8</td><td>Y</td><td>L</td><td>c</td><td>a</td><td>j</td><td>X</td><td>X</td><td>M</td><td>Y</td><td>X</td><td>X</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>q</td><td>T</td><td>W</td><td>B</td><td>w</td><td>P</td><td>B</td><td>H</td><td>W</td></tr>
   <tr><td>0</td><td>b</td><td>E</td><td>p</td><td>X</td><td>u</td><td>l</td><td>Z</td><td>p</td><td>E</td><td>Z</td><td>E</td></tr>
   <tr><td>7</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td><td>K</td><td>V</td><td>K</td></tr>
 </table>

<input id="slider" type="range" value="0" min="0" step="1">

<script>

(function() {
 "use strict";

 var onlyPairs = true,
     t1 = document.getElementById('table1'),
     t2 = document.getElementById('table2'),
     inp = document.getElementById('slider');

 var arr1 = ArrayFromTable(t1),
     arr2 = ArrayFromTable(t2),
     t1cols = arr1[0].length,
     t2cols = arr2[0].length;

 t1.addEventListener('mouseover', highlight.bind(t1, t2, arr1, arr2, true));
 t2.addEventListener('mouseover', highlight.bind(t2, t1, arr2, arr1, true));
 t1.addEventListener('mouseout', highlight.bind(t1, t2, arr1, arr2, false));
 t2.addEventListener('mouseout', highlight.bind(t2, t1, arr2, arr1, false));
 t1.addEventListener('click', select);
 t2.addEventListener('click', select);

 inp.addEventListener('input', scrollCells);
 inp.addEventListener('wheel', wheel);
 t2.addEventListener('wheel', wheel);

 inp.max = t2cols - t1cols;
 scrollCells();

 for(var i=0; i<arr1.length; i++) {
  highlightTableCells(t1, arr1, i, arr1[i][0], true);
  highlightTableCells(t2, arr2, i, arr1[i][0], true);
  t1.tBodies[0].rows[i].cells[0].click();
 }
 addClassToColumn('highlight', t1, 0, 1, false);
 addClassToColumn('highlight', t2, 0, 1, false);

 function ArrayFromTable(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).map(function(cell) {
    return cell.textContent;
   });
  });
 };

 function scrollCells() {
  var index = +inp.value;
  for(var i=1; i<=t2cols; i++) {
    addClassToColumn('hidden', t2, i, i, (i <= index || i > index + t1cols));
    addClassToColumn('highlight', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'highlight'));
    addClassToColumn('selectedCol', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'selectedCol'));
  }
 };

 function wheel(e) {
  inp.value = +inp.value + (e.deltaY > 0 ? 1 : -1);
  scrollCells();
 };

 function toggleClass(node) {
  node.classList.toggle(this.class, this.mode);
 };

 function hasClass(node, className) {
  return node != null && node.classList.contains(className);
 };

 function addClassToColumn(className, tbl, indexTH, indexTD, mode) {
  tbl.querySelectorAll(`th:nth-of-type(${indexTH}), td:nth-of-type(${indexTD})`).forEach(function(node){
   node.classList.toggle(className, mode);
  });
 };

 var highlightColumn = addClassToColumn.bind(null, 'highlight'),
     selectColumn = addClassToColumn.bind(null, 'selectedCol');

 function highlightTableCells(tbl, arr, r, char, mode) {
  for(var c=0; c<arr[r].length; c++) {
   if(arr[r][c] == char) {
    tbl.tBodies[0].rows[r].cells[c].classList.toggle('highlight', mode);
   }
  }
 };

 function highlight(tbl, arrA, arrB, mode, event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   // highlightColumn(t1, index, index, mode);
   highlightColumn(t2, index, index + +inp.value, mode);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      char = event.target.textContent;

  highlightTableCells(this, arrA, r, char, mode);
  highlightTableCells(tbl, arrB, r, char, mode);
 };

 function select(event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   // selectColumn(t1, index, index);
   selectColumn(t2, index, index + +inp.value);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      a = t1.tBodies[0].rows[r].querySelectorAll('.selected'),
      A = t1.tBodies[0].rows[r].querySelectorAll('.highlight'),
      b = t2.tBodies[0].rows[r].querySelectorAll('.selected'),
      B = t2.tBodies[0].rows[r].querySelectorAll('.highlight');

  a.forEach(toggleClass, {class: 'selected', mode: false});
  b.forEach(toggleClass, {class: 'selected', mode: false});

  if((!onlyPairs || A.length && B.length) && !(hasClass(a[0], 'highlight') || hasClass(b[0], 'highlight'))) {
   A.forEach(toggleClass, {class: 'selected', mode: true});
   B.forEach(toggleClass, {class: 'selected', mode: true});
  }
 };

}());

</script>
</body>
</html>```
1 Like

That’s totally knockout alperquin - you’re so amazing! I can’t thank you enough for that… thank you, thank you, thank you… [bow] …
All the best to you… you rock!!
A million thanks!
77713

Hi,
Thanks to alperquin T1 and T2 are working very well together.
Although, it seems the number of columns in T1 govern the number of columns in T2: if I remove a column in T1, a column is removed in T2. I would like to add an extra one or two COLUMNS in T1 only (or remove one from T2) but can’t seem to grasp how to do this.

Also, I wonder if it’s possible to do the following (T1 can best be disregarded for now);

There is a third, vertically-scrolling table (T3), having a fixed header, and multiple rows with some cells containing data.

Disregarding the leftmost column of T3 (Name data):
If there is a SELECTED CELL in say COLUMNS 5, 6 and 7 of T2, then, if there is a ROW in T3 that has DATA in those same COLUMNS, that T3 ROW should highlight (and preferably come into view by scrolling to the top, if possible). Only one row, if any, will ever match;

e.g. If there is a cell selected in both columns ‘2’ and ‘5’ of T2, then the matching T3 row ‘Name 1’ will be highlighted.
If T2 is now scrolled one position leftward then columns ‘1’ and ‘4’ will now have that data in them and the matching row ‘Name 7’ will highlight and scroll into view (‘Name 1’ will be un-highlighted). No match = no highlight.

Thank you once again for all your help.
Best regards, 77713

Please see 3fables.html (7.9 KB)

<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">

<title>table stuff</title>

<style media="screen">

table {
 border-collapse: collapse;
 table-layout: fixed;
 width: 800px;
}

#slider {
 width: 800px;
 transform: rotate(180deg);
}

td, th {
 padding: 0.4em;
 border: 1px solid #999;
}

.highlight, .highlight.selectedCol {
    background-color: #fc9; 
}

.selected, .selected.selectedCol {
    background-color: #9ce; 
}

.selectedCol {
    background-color: rgba(153,	204, 238, .5);
    background-color: #99ccee77;
}

td.hidden { 
 display: none;
}


#scrolltable { margin-top: 30px; height: 200px; overflow: auto; }
#scrolltable table { border-collapse: collapse; }
#scrolltable tr:nth-child(even) { background: #EEE; }
#scrolltable th div { position: absolute; margin-top: -30px; }

</style>

</head>
<body> 



 <h2>Table 1</h2>

 <table id="table1">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>	
   <tr><td>9</td><td>k</td><td>N</td><td>F</td><td>p</td><td>R</td><td>u</td><td>b</td><td>S</td></tr>
   <tr><td>6</td><td>Y</td><td>L</td><td>v</td><td>X</td><td>M</td><td>c</td><td>a</td><td>j</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>W</td><td>B</td><td>w</td><td>P</td><td>q</td><td>T</td></tr>
   <tr><td>0</td><td>u</td><td>b</td><td>E</td><td>O</td><td>X</td><td>l</td><td>Z</td><td>p</td></tr>
   <tr><td>3</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td></tr>
  </tbody>
 </table>

<h2>Table 2</h2>

 <table id="table2">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>
   <tr><td>9</td><td>k</td><td>R</td><td>u</td><td>N</td><td>F</td><td>p</td><td>b</td><td>S</td><td>N</td><td>F</td><td>S</td></tr>
   <tr><td>8</td><td>Y</td><td>L</td><td>c</td><td>a</td><td>j</td><td>X</td><td>X</td><td>M</td><td>Y</td><td>X</td><td>X</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>q</td><td>T</td><td>W</td><td>B</td><td>w</td><td>P</td><td>B</td><td>H</td><td>W</td></tr>
   <tr><td>0</td><td>b</td><td>E</td><td>p</td><td>X</td><td>u</td><td>l</td><td>Z</td><td>p</td><td>E</td><td>Z</td><td>E</td></tr>
   <tr><td>7</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td><td>K</td><td>V</td><td>K</td></tr>
 </table>

<input id="slider" type="range" value="0" min="0" step="1">

<h2>Table 3</h2>
<div id="scrolltable">
    <table>
        <tr><th><div>NAME</div></th><th><div>0</div></th><th><div>1</div></th><th><div>2</div></th><th><div>3</div></th><th><div>4</div></th><th><div>5</div></th><th><div>6</div></th><th><div>7</div></th><th><div>8</div></th></tr>
        <tr><td>Name 1</td><td></td><td></td><td>bar</td><td></td><td></td><td>foo</td><td></td><td></td><td></td></tr>
        <tr><td>Name 2</td><td></td><td></td><td>bar</td><td>foo</td><td>bar</td><td></td><td>bar</td><td></td><td>foo</td></tr>
        <tr><td>Name 3</td><td>bar</td><td></td><td>bar</td><td></td><td>bar</td><td>foo</td><td></td><td></td><td></td></tr>
        <tr><td>Name 4</td><td></td><td></td><td></td><td></td><td>bar</td><td></td><td>bar</td><td>foo</td><td></td></tr>
        <tr><td>Name 5</td><td></td><td></td><td></td><td>foo</td><td>bar</td><td></td><td></td><td>foo</td><td></td></tr>
        <tr><td>Name 6</td><td>bar</td><td></td><td></td><td>foo</td><td>bar</td><td>foo</td><td>bar</td><td>foo</td><td></td></tr>
        <tr><td>Name 7</td><td></td><td>foo</td><td></td><td></td><td>bar</td><td></td><td></td><td></td><td></td></tr>
        <tr><td>Name 8</td><td>bar</td><td></td><td></td><td>foo</td><td>bar</td><td>foo</td><td></td><td>foo</td><td></td></tr>
        <tr><td>Name 9</td><td></td><td></td><td></td><td>foo</td><td></td><td>foo</td><td></td><td>foo</td><td>foo</td></tr>
    </table>
</div>


<script>

(function() {
 "use strict";

 var onlyPairs = true,
     t1 = document.getElementById('table1'),
     t2 = document.getElementById('table2'),
     inp = document.getElementById('slider');

 var arr1 = ArrayFromTable(t1),
     arr2 = ArrayFromTable(t2),
     t1cols = arr1[0].length,
     t2cols = arr2[0].length;

 t1.addEventListener('mouseover', highlight.bind(t1, t2, arr1, arr2, true));
 t2.addEventListener('mouseover', highlight.bind(t2, t1, arr2, arr1, true));
 t1.addEventListener('mouseout', highlight.bind(t1, t2, arr1, arr2, false));
 t2.addEventListener('mouseout', highlight.bind(t2, t1, arr2, arr1, false));
 t1.addEventListener('click', select);
 t2.addEventListener('click', select);

 inp.addEventListener('input', scrollCells);
 inp.addEventListener('wheel', wheel);
 t2.addEventListener('wheel', wheel);

 inp.max = t2cols - t1cols;
 scrollCells();

 for(var i=0; i<arr1.length; i++) {
  highlightTableCells(t1, arr1, i, arr1[i][0], true);
  highlightTableCells(t2, arr2, i, arr1[i][0], true);
  t1.tBodies[0].rows[i].cells[0].click();
 }
 addClassToColumn('highlight', t1, 0, 1, false);
 addClassToColumn('highlight', t2, 0, 1, false);

 function ArrayFromTable(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).map(function(cell) {
    return cell.textContent;
   });
  });
 };

 function scrollCells() {
  var index = +inp.value;
  for(var i=1; i<=t2cols; i++) {
    addClassToColumn('hidden', t2, i, i, (i <= index || i > index + t1cols));
    addClassToColumn('highlight', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'highlight'));
    addClassToColumn('selectedCol', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'selectedCol'));
  }
 };

 function wheel(e) {
  inp.value = +inp.value + (e.deltaY > 0 ? 1 : -1);
  scrollCells();
 };

 function toggleClass(node) {
  node.classList.toggle(this.class, this.mode);
 };

 function hasClass(node, className) {
  return node != null && node.classList.contains(className);
 };

 function addClassToColumn(className, tbl, indexTH, indexTD, mode) {
  tbl.querySelectorAll(`th:nth-of-type(${indexTH}), td:nth-of-type(${indexTD})`).forEach(function(node){
   node.classList.toggle(className, mode);
  });
 };

 var highlightColumn = addClassToColumn.bind(null, 'highlight'),
     selectColumn = addClassToColumn.bind(null, 'selectedCol');

 function highlightTableCells(tbl, arr, r, char, mode) {
  for(var c=0; c<arr[r].length; c++) {
   if(arr[r][c] == char) {
    tbl.tBodies[0].rows[r].cells[c].classList.toggle('highlight', mode);
   }
  }
 };

 function highlight(tbl, arrA, arrB, mode, event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   // highlightColumn(t1, index, index, mode);
   highlightColumn(t2, index, index + +inp.value, mode);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      char = event.target.textContent;

  highlightTableCells(this, arrA, r, char, mode);
  highlightTableCells(tbl, arrB, r, char, mode);
 };

 function select(event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   // selectColumn(t1, index, index);
   selectColumn(t2, index, index + +inp.value);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      a = t1.tBodies[0].rows[r].querySelectorAll('.selected'),
      A = t1.tBodies[0].rows[r].querySelectorAll('.highlight'),
      b = t2.tBodies[0].rows[r].querySelectorAll('.selected'),
      B = t2.tBodies[0].rows[r].querySelectorAll('.highlight');

  a.forEach(toggleClass, {class: 'selected', mode: false});
  b.forEach(toggleClass, {class: 'selected', mode: false});

  if((!onlyPairs || A.length && B.length) && !(hasClass(a[0], 'highlight') || hasClass(b[0], 'highlight'))) {
   A.forEach(toggleClass, {class: 'selected', mode: true});
   B.forEach(toggleClass, {class: 'selected', mode: true});
  }
 };

}());

</script>
</body>
</html>```

I changed the code for hiding columns in table 2 and added a function for highlighting rows in table 3.

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">

<title>table stuff</title>

<style media="screen">

table {
 border-collapse: collapse;
 table-layout: fixed;
 width: 800px;
}

#slider {
 width: 800px;
 transform: rotate(180deg);
}

td, th {
 padding: 0.4em;
 border: 1px solid #999;
}

.highlight, .highlight.selectedCol {
    background-color: #fc9; 
}

.selected, .selected.selectedCol, #scrolltable tbody tr.selected {
    background-color: #9ce; 
}

.selectedCol {
    background-color: rgba(153,	204, 238, .5);
    background-color: #99ccee77;
}

td.hidden { 
 display: none;
}


#scrolltable { margin-top: 30px; height: 200px; overflow: auto; }
#scrolltable table { border-collapse: collapse; }
#scrolltable tr:nth-child(even) { background: #EEE; }
#scrolltable th div { position: absolute; margin-top: -30px; }
}

</style>

</head>
<body> 



 <h2>Table 1</h2>

 <table id="table1">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>9</th><th>10</th></tr>
  </thead>
  <tbody>	
   <tr><td>9</td><td>k</td><td>N</td><td>F</td><td>p</td><td>R</td><td>u</td><td>b</td><td>S</td><td>X</td><td>X</td></tr>
   <tr><td>6</td><td>Y</td><td>L</td><td>v</td><td>X</td><td>M</td><td>c</td><td>a</td><td>j</td><td>X</td><td>X</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>W</td><td>B</td><td>w</td><td>P</td><td>q</td><td>T</td><td>X</td><td>X</td></tr>
   <tr><td>0</td><td>u</td><td>b</td><td>E</td><td>O</td><td>X</td><td>l</td><td>Z</td><td>p</td><td>X</td><td>X</td></tr>
   <tr><td>3</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td><td>X</td><td>X</td></tr>
  </tbody>
 </table>

<h2>Table 2</h2>

 <table id="table2">
  <thead>
   <tr><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th></tr>
  </thead>
  <tbody>
   <tr><td>9</td><td>k</td><td>R</td><td>u</td><td>N</td><td>F</td><td>p</td><td>b</td><td>S</td><td>N</td><td>F</td><td>S</td></tr>
   <tr><td>8</td><td>Y</td><td>L</td><td>c</td><td>a</td><td>j</td><td>X</td><td>X</td><td>M</td><td>Y</td><td>X</td><td>X</td></tr>
   <tr><td>4</td><td>H</td><td>h</td><td>q</td><td>T</td><td>W</td><td>B</td><td>w</td><td>P</td><td>B</td><td>H</td><td>W</td></tr>
   <tr><td>0</td><td>b</td><td>E</td><td>p</td><td>X</td><td>u</td><td>l</td><td>Z</td><td>p</td><td>E</td><td>Z</td><td>E</td></tr>
   <tr><td>7</td><td>f</td><td>v</td><td>h</td><td>r</td><td>g</td><td>K</td><td>k</td><td>V</td><td>K</td><td>V</td><td>K</td></tr>
 </table>

<input id="slider" type="range" value="0" min="0" step="1">

<h2>Table 3</h2>
<div id="scrolltable">
    <table id="table3">
      <thead>
        <tr><th><div>NAME</div></th><th><div>0</div></th><th><div>1</div></th><th><div>2</div></th><th><div>3</div></th><th><div>4</div></th><th><div>5</div></th><th><div>6</div></th><th><div>7</div></th><th><div>8</div></th></tr>
      </thead>
      <tbody>
        <tr><td>Name 1</td><td></td><td></td><td>bar</td><td></td><td></td><td>foo</td><td></td><td></td><td></td></tr>
        <tr><td>Name 2</td><td></td><td></td><td>bar</td><td>foo</td><td>bar</td><td></td><td>bar</td><td></td><td>foo</td></tr>
        <tr><td>Name 3</td><td>bar</td><td></td><td>bar</td><td></td><td>bar</td><td>foo</td><td></td><td></td><td></td></tr>
        <tr><td>Name 4</td><td></td><td></td><td></td><td></td><td>bar</td><td></td><td>bar</td><td>foo</td><td></td></tr>
        <tr><td>Name 5</td><td></td><td></td><td></td><td>foo</td><td>bar</td><td></td><td></td><td>foo</td><td></td></tr>
        <tr><td>Name 6</td><td>bar</td><td></td><td></td><td>foo</td><td>bar</td><td>foo</td><td>bar</td><td>foo</td><td></td></tr>
        <tr><td>Name 7</td><td></td><td>foo</td><td></td><td></td><td>bar</td><td></td><td></td><td></td><td></td></tr>
        <tr><td>Name 8</td><td>bar</td><td></td><td></td><td>foo</td><td>bar</td><td>foo</td><td></td><td>foo</td><td></td></tr>
        <tr><td>Name 9</td><td></td><td></td><td></td><td>foo</td><td></td><td>foo</td><td></td><td>foo</td><td>foo</td></tr>
      </tbody>
    </table>
</div>


<script>

(function() {
 "use strict";

 var onlyPairs = true,
     t1 = document.getElementById('table1'),
     t2 = document.getElementById('table2'),
     t3 = document.getElementById('table3'),												/**/
     inp = document.getElementById('slider');

 var arr1 = ArrayFromTable(t1),
     arr2 = ArrayFromTable(t2),
     t1cols = arr1[0].length,
     t2cols = arr2[0].length,
     t2headCols = t2.getElementsByTagName('th').length,											/**/
     t3hashes = hashTable3(t3);														/**/

 t1.addEventListener('mouseover', highlight.bind(t1, t2, arr1, arr2, true));
 t2.addEventListener('mouseover', highlight.bind(t2, t1, arr2, arr1, true));
 t1.addEventListener('mouseout', highlight.bind(t1, t2, arr1, arr2, false));
 t2.addEventListener('mouseout', highlight.bind(t2, t1, arr2, arr1, false));
 t1.addEventListener('click', select);
 t2.addEventListener('click', select);

 inp.addEventListener('input', scrollCells);
 inp.addEventListener('wheel', wheel);
 t2.addEventListener('wheel', wheel);

 // inp.max = t2cols - t1cols;														/**/
 inp.max = t2cols - t2headCols;														/**/
 scrollCells();

 for(var i=0; i<arr1.length; i++) {
  highlightTableCells(t1, arr1, i, arr1[i][0], true);
   highlightTableCells(t2, arr2, i, arr1[i][0], true);
  t1.tBodies[0].rows[i].cells[0].click();
 }

 addClassToColumn('highlight', t1, 0, 1, false);
 addClassToColumn('highlight', t2, 0, 1, false);

 function ArrayFromTable(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).map(function(cell) {
    return cell.textContent;
   });
  });
 };

 function hashTable3(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).slice(1).reduce(function(hash, cell, idx) {
    return hash + (cell.textContent != '' ? idx : '');
   }, '');
  });
 };

 function scrollCells() {
  var index = +inp.value;
  for(var i=1; i<=t2cols; i++) {
    // addClassToColumn('hidden', t2, i, i, (i <= index || i > index + t1cols));							/**/
    addClassToColumn('hidden', t2, i, i, (i <= index || i > index + t2headCols));							/**/
    addClassToColumn('highlight', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'highlight'));
    addClassToColumn('selectedCol', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'selectedCol'));
  }
  matchTable3();															/**/
 };

 function wheel(e) {
  inp.value = +inp.value + (e.deltaY > 0 ? 1 : -1);
  scrollCells();
 };

 function toggleClass(node) {
  node.classList.toggle(this.class, this.mode);
 };

 function hasClass(node, className) {
  return node != null && node.classList.contains(className);
 };

 function addClassToColumn(className, tbl, indexTH, indexTD, mode) {
  tbl.querySelectorAll(`th:nth-of-type(${indexTH}), td:nth-of-type(${indexTD})`).forEach(function(node){
   node.classList.toggle(className, mode);
  });
 };

 var highlightColumn = addClassToColumn.bind(null, 'highlight'),
     selectColumn = addClassToColumn.bind(null, 'selectedCol');

 function highlightTableCells(tbl, arr, r, char, mode) {
  for(var c=0; c<arr[r].length; c++) {
   if(arr[r][c] == char) {
    tbl.tBodies[0].rows[r].cells[c].classList.toggle('highlight', mode);
   }
  }
 };

 function highlight(tbl, arrA, arrB, mode, event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   // highlightColumn(t1, index, index, mode);
   highlightColumn(t2, index, index + +inp.value, mode);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      char = event.target.textContent;

  highlightTableCells(this, arrA, r, char, mode);
  highlightTableCells(tbl, arrB, r, char, mode);
 };

 function select(event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   // selectColumn(t1, index, index);
   selectColumn(t2, index, index + +inp.value);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      a = t1.tBodies[0].rows[r].querySelectorAll('.selected'),
      A = t1.tBodies[0].rows[r].querySelectorAll('.highlight'),
      b = t2.tBodies[0].rows[r].querySelectorAll('.selected'),
      B = t2.tBodies[0].rows[r].querySelectorAll('.highlight');

  a.forEach(toggleClass, {class: 'selected', mode: false});
  b.forEach(toggleClass, {class: 'selected', mode: false});

  if((!onlyPairs || A.length && B.length) && !(hasClass(a[0], 'highlight') || hasClass(b[0], 'highlight'))) {
   A.forEach(toggleClass, {class: 'selected', mode: true});
   B.forEach(toggleClass, {class: 'selected', mode: true});
  }

  matchTable3();															/**/
 };

 function matchTable3() {														/**/
  var indexes = Array.from(t2.querySelectorAll('td.selected')).map(function(cell) {
   return cell.cellIndex - inp.value;
  }).filter(function(v) {
   return v >= 0 && v < t2headCols;
  }).sort(function(a, b) {
   return a-b;
  }).join('');

  var rowIdx = t3hashes.indexOf(indexes);

  t3.querySelectorAll('tr.selected').forEach(toggleClass, {class: 'selected', mode: false});
  if(~rowIdx) {
   t3.tBodies[0].rows[rowIdx].classList.toggle('selected', true);
   t3.tBodies[0].rows[rowIdx].scrollIntoView();
  }
 };

}());

</script>
</body>
</html>
1 Like

Oohh… that’s exactly how wished it to be - I’m delighted - thanks to you yet again alperquin - you’re such a genius! Very kind of you.
All the best!
77713

Hi everyone,

Everything is working fantastic, much thanks to alperquin.

However, I just realised that it would function even better if it was possible to make a few modifications to the operation of table 1, but I am (unsurprisingly) having great difficulty figuring just how to do this - any help would be greatly appreciated.

Re Table 1:

  1. When the html doc is opened (or refreshed), the cells in the SECOND column of Table 1 (the column 2nd from left) would be (pre-)selected (not those in the first column as it is at the moment).

  2. When ANY SELECTED CELL in any column EXCEPT the SECOND column, is de-selected (by clicking it in Table 1), the cell in the SECOND column becomes selected.

  3. When a selected cell in the SECOND column is de-selected (by clicking it in Table 1), the cell in The FIRST column becomes selected.

i.e.

  • cols 1 and 2 (from left) can be SELECTED as normal (same as the others);
  • de-selecting a selected cell except col 2, causes the col 2 cell to select;
  • de-selecting a selected col 2 cell causes the col 1 cell to select.
    (Selecting/de-selecting via Table 2 works as is.)

I hope the CAPS are ok - I just find it easier to read.

Thank you again.
I wish you all the best for the festivities and beyond.
77713

Hi, sorry to say but I seem to have got it a little bit wrong when I said:

I should’ve more precisely said - If there is ONE OR MORE selected cell…

As a result, it functions fine when there is only one cell per column selected in T2, but concurrently selecting 2 (or more) cells in a same T2 column causes the row highlighting (in T3) to be removed.

So sorry for not realizing I wasn’t being totally specific.

Is this easy to fix? I’ve tried but not quite got it yet.

Cheers
77713

This is giving me some trouble. When the function to select the cells in the second column is executed, it also selects the highlighted cells that were just de-selected. I’ll have to figure out a way to prevent that from happening.

Yes, it is.

  var indexes = Array.from(t2.querySelectorAll('td.selected'));
  indexes = indexes.map(function(cell) {
   return cell.cellIndex - inp.value;
  });
  indexes = indexes.filter(function(v) {
   return v >= 0 && v < t2headCols;
  });
  indexes.sort(function(a, b) {
   return a-b;
  });
  indexes = Array.from(new Set(indexes));
  indexes = indexes.join('');

  var rowIdx = t3hashes.indexOf(indexes);

  t3.querySelectorAll('tr.selected').forEach(toggleClass, {class: 'selected', mode: false});
  if(~rowIdx) {
   t3.tBodies[0].rows[rowIdx].classList.toggle('selected', true);
   t3.tBodies[0].rows[rowIdx].scrollIntoView();
  }
 };
1 Like

I got it practically working.

  • What should happen when you de-select a cell in column 2, when there is no match for column 1 on that row? Currecntly it keeps column 2 selected.
  • What should happen when when a cell in column 2 of table 1 gets de-selected by clicking on a cell in table 2? Now it selects column 1 of table 1, just like de-selecting column2 of table 1.

(function() {
 "use strict";

 var onlyPairs = true,
     t1 = document.getElementById('table1'),
     t2 = document.getElementById('table2'),
     t3 = document.getElementById('table3'),
     inp = document.getElementById('slider'),
     primarySelectRow = 1,
     secondarySelectRow = 0;

 var arr1 = ArrayFromTable(t1),
     arr2 = ArrayFromTable(t2),
     t1cols = arr1[0].length,
     t2cols = arr2[0].length,
     t2headCols = t2.getElementsByTagName('th').length,
     t3hashes = hashTable3(t3);

 t1.addEventListener('mouseover', highlight.bind(t1, t2, arr1, arr2, true));
 t2.addEventListener('mouseover', highlight.bind(t2, t1, arr2, arr1, true));
 t1.addEventListener('mouseout', highlight.bind(t1, t2, arr1, arr2, false));
 t2.addEventListener('mouseout', highlight.bind(t2, t1, arr2, arr1, false));
 t1.addEventListener('click', select);
 t2.addEventListener('click', select);

 inp.addEventListener('input', scrollCells);
 inp.addEventListener('wheel', wheel);
 t2.addEventListener('wheel', wheel);

 inp.max = t2cols - t2headCols;
 scrollCells();

 // Preselect the second column
 for(var i=0; i<arr1.length; i++) {
  selectTargetCell(t1, i, primarySelectRow);
 }

 function selectTargetCell(tbl, row, idx) {
   highlightTableCells(t1, arr1, row, arr1[row][idx], true);  // find and highlight matches
   highlightTableCells(t2, arr2, row, arr1[row][idx], true);
   tbl.tBodies[0].rows[row].cells[idx].click();               // selected cells
   highlightTableCells(t1, arr1, row, arr1[row][idx], false); // remove highlight
   highlightTableCells(t2, arr2, row, arr1[row][idx], false);
 };

 function ArrayFromTable(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).map(function(cell) {
    return cell.textContent;
   });
  });
 };

 function hashTable3(tbl) {
  return Array.from(tbl.tBodies[0].rows).map(function(row) {
   return Array.from(row.cells).slice(1).reduce(function(hash, cell, idx) {
    return hash + (cell.textContent != '' ? idx : '');
   }, '');
  });
 };

 function scrollCells() {
  var index = +inp.value;
  for(var i=1; i<=t2cols; i++) {
    addClassToColumn('hidden', t2, i, i, (i <= index || i > index + t2headCols));
    addClassToColumn('highlight', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'highlight'));
    addClassToColumn('selectedCol', t2, i, i + +inp.value, hasClass(t2.querySelector(`th:nth-of-type(${i})`), 'selectedCol'));
  }
  matchTable3();
 };

 function wheel(e) {
  inp.value = +inp.value + (e.deltaY > 0 ? 1 : -1);
  scrollCells();
 };

 function toggleClass(node) {
  node.classList.toggle(this.class, this.mode);
 };

 function hasClass(node, className) {
  return node != null && node.classList.contains(className);
 };

 function addClassToColumn(className, tbl, indexTH, indexTD, mode) {
  tbl.querySelectorAll(`th:nth-of-type(${indexTH}), td:nth-of-type(${indexTD})`).forEach(function(node){
   node.classList.toggle(className, mode);
  });
 };

 var highlightColumn = addClassToColumn.bind(null, 'highlight'),
     selectColumn = addClassToColumn.bind(null, 'selectedCol');

 function highlightTableCells(tbl, arr, r, char, mode) {
  for(var c=0; c<arr[r].length; c++) {
   if(arr[r][c] == char) {
    tbl.tBodies[0].rows[r].cells[c].classList.toggle('highlight', mode);
   }
  }
 };

 function highlight(tbl, arrA, arrB, mode, event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   highlightColumn(t2, index, index + +inp.value, mode);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      char = event.target.textContent;

  highlightTableCells(this, arrA, r, char, mode);
  highlightTableCells(tbl, arrB, r, char, mode);
 };

 function select(event) {
  if(this == t2 && event.target.tagName == 'TH') {
   var index = event.target.cellIndex + 1;
   selectColumn(t2, index, index + +inp.value);
  }

  if(event.target.tagName != 'TD') {
   return;
  }

  var r = event.target.parentNode.sectionRowIndex,
      a = t1.tBodies[0].rows[r].querySelectorAll('.selected'),
      A = t1.tBodies[0].rows[r].querySelectorAll('.highlight'),
      b = t2.tBodies[0].rows[r].querySelectorAll('.selected'),
      B = t2.tBodies[0].rows[r].querySelectorAll('.highlight');

  a.forEach(toggleClass, {class: 'selected', mode: false});
  b.forEach(toggleClass, {class: 'selected', mode: false});

  // select cells if there are matches, else select column 2 or 1 when de-selecting column 2
  if((!onlyPairs || A.length && B.length) && !(hasClass(a[0], 'highlight') || hasClass(b[0], 'highlight'))) {
   A.forEach(toggleClass, {class: 'selected', mode: true});
   B.forEach(toggleClass, {class: 'selected', mode: true});
  }
  else { // no matches or de-selecting
   A.forEach(toggleClass, {class: 'highlight', mode: false}); // remove all highlights, the clicked cell should not be selected
   B.forEach(toggleClass, {class: 'highlight', mode: false});

   selectTargetCell(t1, r, A.length && B.length && a[0].cellIndex == 1 ? secondarySelectRow : primarySelectRow);

   A.forEach(toggleClass, {class: 'highlight', mode: true}); // add back highlight, so this cell can be clicked without moving the mouse first
   B.forEach(toggleClass, {class: 'highlight', mode: true});
  }

  matchTable3();
 };

 function matchTable3() {
  var indexes = Array.from(t2.querySelectorAll('td.selected'));
  indexes = indexes.map(function(cell) {
   return cell.cellIndex - inp.value;
  });
  indexes = indexes.filter(function(v) {
   return v >= 0 && v < t2headCols;
  });
  indexes.sort(function(a, b) {
   return a-b;
  });
  indexes = Array.from(new Set(indexes));
  indexes = indexes.join('');

  var rowIdx = t3hashes.indexOf(indexes);

  t3.querySelectorAll('tr.selected').forEach(toggleClass, {class: 'selected', mode: false});
  if(~rowIdx) {
   t3.tBodies[0].rows[rowIdx].classList.toggle('selected', true);
   t3.tBodies[0].rows[rowIdx].scrollIntoView();
  }
 };

}());

</script>
1 Like

That’s simply amazing Alperquin!

Thank you very much. Once again you’ve got it exactly as I’d hoped for.

I realised it was a fairly complex thing to impart with clarity, but you understood my attempt perfectly and you made an absolutely wonderful job of it. That certainly wasn’t easy to figure out and I am so impressed by your outstanding expertise and very grateful indeed for your kindness in helping me with my project. Thanks to you Alperquin it’s coming together beautifully now and I’m over the Moon.

I thoroughly appreciate all your precious time and effort expended on my project - most excellent of you Alperquin - you’ve made my day once again.

Best regards, 77713

P.s. I’m so sorry for the delay in replying - I was simply unable to attend to it in good time due to some other pressing issues.