Table column content to apply style

Currently I have a boolean table col(umn) as the 2nd col and I manually change the 1st col style to reflect this, as in “word_on” and “word_off”. I have multiple styles depending on the 1st col, which has for selector td.something.word_off. Everytime I set col 2 to ‘1’ (bool on; as in <td>1</td>), I have to remember to change the class correspondingly (word_off to word_on) and I tend to forget it.

Optionally there can be error handling, which displays a message is there’s anything in col 2 other than 0 or 1.

I’d like for an ECMAScript to take care of this automatically.

Thank you kindly

Code like the following is what you are after, but be warned. I have typed this out on an iPad with much cursing and swearing involved, and no ability to test if it actually works or not,

I’ll come back to this when I have a proper computing device.

function getPrevCell(input) {
  return input.parentNode.previousSiblingElement;
}
function inputChangeHandler(evt) {
  const input = evt.target;
  const prevCell = getPrevCell(input);
  prevCell.classList.remove("word_on");
  prevCell.classList.remove("word_off");
  prevCell.classList.remove("error");
  if (input.value === "1") {
    prevCell.classList.add("word_on");
  } else if (input.value === "0") {
    prevCell.classList.add("word_off");
  } else {
    prevCell.classList.add("error");
  }
}
const table = document.querySelector("table");
table.querySelector("input").forEach(function (input) {
  input.addEventListener("change", inputChangeHandler);
  inputChangeHandler({target: input});
});

I have a couple concern:

  1. Is there a way to handle columns so the code doesn’t slow down for other columns? I’m not sure if through DOM that would be quick; if not perhaps going a regex on td rows to grab only the 1st 2 cells?
  2. I’m assuming the classList removals are done in DOM, is that done quickly?

Please note I’m quite the beginner in ECMAScript so my concerns may totally be invalid.

Yes, the elements that addEventListener happens to can be updated so that it only occurs to input elements in column two.

It would slightly faster if multiple classes weren’t used. For example, if a default style was used for one of the situations such as off, and another style was used for on. But that kind of improvement is not detectable by human senses.

Here’s a rundown of working code, at least on my test page. Things might be more suitable for you if you supply suitable content for a test page instead.

The table body is obtained from the tBodies property of the table. That helps us to ignore any header or footer that you might have on the table.

const table = document.querySelector("table");
const tbody = table.tBodies[0];

The second column is accessed by getting the nth-of-type for the td element.

const secondCol = tbody.querySelectorAll("td:nth-of-type(2) input");

We can then add an event to each of those inputs when the page loads, so that our code in the inputChangeHandler function runs only when the input is changed.

The second call to inputChangeHandler ensures that the styles are applied when the page loads too.

secondCol.forEach(function (input) {
    input.addEventListener("input", inputChangeHandler);
    inputChangeHandler({target: input});
});

Using the input event lets the update happen as you type, instead of waiting for the change event to trigger when you leave the input field.

The inputChangeHandler function gets the input that has changed, and based on that it also gets the previous cell.

function inputChangeHandler(evt) {
    const input = evt.target;
    const firstCell = getFirstCell(input);
    ...
}

The getPrevCell function just goes up in the DOM from the input to its parent, that being the second td element of the row, and from that to the previous sibling of that td to get the first td of the row.

function getFirstCell(input) {
    const tr = input.parentNode.parentNode;
    return tr.children[0];
}

The inputChangeHandler can then update the style of that first cell.

function inputChangeHandler(evt) {
    const input = evt.target;
    const firstCell = getFirstCell(input);
    updateStyle(firstCell, input);
}

where that updateStyle function has a simple task to perform. It removes any old classnames that might be on it, and adds a new classname.

function updateStyle(cell, input) {
    cell.classList.remove("word_on");
    cell.classList.remove("word_off");
    cell.classList.remove("error");
    if (input.value === "0") {
        cell.classList.add("word_off");
    } else if (input.value === "1") {
        cell.classList.add("word_on");
    } else {
        cell.classList.add("error");
    }
}

And that’s all you need.

You can see it in action at the following test page: http://jsfiddle.net/gnf1aye7/2/

I’m sorry I wasn’t clear, I don’t need something interactive, simply on (re)load is sufficient. I was looking at javascript - How to get text value of specific columns in a table for each row? - Stack Overflow answered Aug 17 '17 at 5:22 by qiAlex (user #8131579) and it look like a good base. In this circumstance I’m wondering if an event listener will be necessary. From the very little I grasp at the concept, I think a procedural approach would be sufficient, perhaps even more efficient; actually, unless the more efficient method is a decent amount more complicated, I’d prefer that.

For the error, I was–very–unclear but I’d like for the bool col(umn), which is the 2nd col (col 2). to be highlighted (so a class added I assume). I think I could manage it through CSS to have the error class in col 1 but it affecting col 2. I’m rusty on CSS but IIRC their selectors were decently “powerful”. Optionally, as soon as there’s 1 error, a message to be shown (ie: Occurrences in the 2nd column which are [describe style] indicate their content is invalid, which should be either ‘0’ or ‘1’.), perhaps right before the table.

I love to see your custom examples, but if you think there’s something already out there which closely fit what I’m looking for, if you want you could point me to it and describe what I should look for in it.

Update 1:

I ended up doing it by myself in the following, although it’s quite likely there’s better ways to do it, perhaps you could comment on it?

<script>
var firstTdArr = document.querySelectorAll("td:first-child");
var secondTdArr = document.querySelectorAll("td:nth-child(2)");

for (var i = 0; i<firstTdArr.length; i++) {
	var toLog = 'td1 #' + i + ', td 2 case: ';
	firstTdArr[i].classList.remove("word_on");
	firstTdArr[i].classList.remove("word_off");
	firstTdArr[i].classList.remove("error");
	if (secondTdArr[i].innerText == "0") {
		toLog += '0';
		firstTdArr[i].classList.add("word_off");
	} else if (secondTdArr[i].innerText == "1") {
		toLog += '1';
		firstTdArr[i].classList.add("word_on");
	} else {
		toLog += 'neither';
		firstTdArr[i].classList.add("error");
	}
	console.log (toLog);
}
</script>
</body>

An example of the resulting console content:

td1 #0, td 2 case: 1 FILENAME:216:10
td1 #1, td 2 case: 0 FILENAME:216:10
td1 #2, td 2 case: 0 FILENAME:216:10
td1 #3, td 2 case: 0 FILENAME:216:10
td1 #4, td 2 case: 0 FILENAME:216:10
td1 #5, td 2 case: 1 FILENAME:216:10
td1 #6, td 2 case: 0 FILENAME:216:10
td1 #7, td 2 case: 1 FILENAME:216:10
td1 #8, td 2 case: 0 FILENAME:216:10
td1 #9, td 2 case: 0 FILENAME:216:10
td1 #10, td 2 case: 1 FILENAME:216:10
td1 #11, td 2 case: 1 FILENAME:216:10
td1 #12, td 2 case: 0 FILENAME:216:10
td1 #13, td 2 case: 1 FILENAME:216:10

Although I haven’t figured the error display yet as I’m quite tired.