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>