How to add a table cell to a table row in JavaScript?

I want to add a table cell in the begining of any table row. I have tried this:

const tableRow = document.querySelectorAll('.tr');
const firstNewCell = document.createElement('td');

tableRow.forEach((element) => {
    element.insertAdjacentElement("beforebegin", firstNewCell)
});

firstNewCell.style.display = "table-cell";
firstNewCell.style.width = "500px";
firstNewCell.style.backgroundColor = "yellow";
firstNewCell.innerHTML = "A";

No new td element is added to any tr in the DOM, and the only console ouput is:

‘A’

What have I done wrong?

unless you have this markup, it’s this line…

<tr class="tr">.....</tr>

Remove the period (or add the class, whichever), and it generates the element. Should get you started from there…

1 Like

So going through your code

// needs to be tr not a classname of .tr
const tableRow = document.querySelectorAll('.tr');
const firstNewCell = document.createElement('td');

tableRow.forEach((element) => {
   // needs to be "afterbegin", so that it is just inside the beginning of the table row element
   // you are also using the same firstNewCell element each time so rather than it being
   // duplicated it will be moved from one table row to the next.
   // you need to create a table cell element inside this function
   element.insertAdjacentElement("beforebegin", firstNewCell)
});

// this should be inside the forEach callback function

firstNewCell.style.display = "table-cell";
firstNewCell.style.width = "500px";
firstNewCell.style.backgroundColor = "yellow";
firstNewCell.innerHTML = "A";

So something like this

const tableRows = document.querySelectorAll('tr');

tableRows.forEach((element) => {
  const firstNewCell = document.createElement('td');
  
  firstNewCell.style.display = "table-cell";
  firstNewCell.style.width = "500px";
  firstNewCell.style.backgroundColor = "yellow";
  firstNewCell.innerHTML = "A";
  
  element.insertAdjacentElement("afterbegin", firstNewCell)
});

You weren’t far off, just a few small alterations and shuffling of code about.

Just as an option, you could always wrap the creation of the table cell inside a function of it’s own.

const tableRows = document.querySelectorAll('tr');

function createTableCell (content = "") {
  const tableCell = document.createElement('td');
  
  tableCell.style.display = "table-cell";
  tableCell.style.width = "500px";
  tableCell.style.backgroundColor = "yellow";
  tableCell.innerHTML = content;
  // return the new tableCell
  return tableCell
}

tableRows.forEach((element) => {
  const firstNewCell = createTableCell("A");
  element.insertAdjacentElement("afterbegin", firstNewCell)
});
4 Likes

Another issue is that you’re adding the same cell element to each row, thus removing it from each previous row (an element can only have a single parent), and ending up with the new cell only being added to the last row. You’d have to create a new cell inside the forEach() callback for this to work.

BTW you might also have a look at the insertCell() method, which makes things a bit less verbose:

const tableRows = document.querySelectorAll('tr')

tableRows.forEach(row => {
  const newCell = row.insertCell(0)
  // Set style and text content here
})

(x-post)

3 Likes

I would like to thank all commenters for their remarks.

Finally I came up with the following working tested code which you can test in any owned Drupal website at:

example.com/admin/structure/types

Code

const allTableRowsExceptTableHeading = document.querySelectorAll('tr.odd, tr.even');

allTableRowsExceptTableHeading.forEach((element) => {
    const newCell = document.createElement('td');
    // This variable must be here inside the function because...
        // For each table row we work on, we create and add a table cell...

    element.insertAdjacentElement('afterbegin', newCell);

    newCell.innerHTML = '<span class="numberCell">A</span>';
    newCell.style.display = 'table-cell';
    newCell.style.width = 'maxContent';
    newCell.style.textAlign = 'center';
    newCell.style.backgroundColor = 'yellow';
    // These HTML-CSS directives must be here inside the function because...
        // For each table row we work on, we create and add these new HTML-CSS directives...
});

let baseNumber = 1;
document.querySelectorAll('.numberCell').forEach((element)=>{
    element.innerHTML = `<span>${baseNumber++}</span>`;
});

Final outcome

Notes

  • I needed 'tr.odd, tr.even' to select all tr elements which are not the first tr which is a table heading because regular negation as with 'tr:not(:first-child)' didn’t work for some reason possibly related to Drupal’s HTML-CSS behavior.
  • As can be figured from the code, the last part makes the new column and all its cells to include one-base-indexed incremented numbers, as a way to directly output a count of all existing rows.
    But,
    There might be a better way to do it.
1 Like

Well that’s probably because the header row is correctly wrapped in a thead with only that single child row. Maybe try tbody > tr as a selector instead, so you don’t have to rely on class names related to styling.

You might do this directly inside the forEach() callback, which receives the current index as the second parameter:

allTableRowsExceptTableHeading.forEach((element, index) => {
  const newCell = document.createElement('td')

  element.insertAdjacentElement('afterbegin', newCell)
  newCell.innerHTML = `<span class="numberCell">${index + 1}</span>`
  // ...
})

Or maybe use a CSS counter, which would have the advantage of updating automatically when rows are getting added / removed:

tbody {
  counter-reset: number-cell;
}

.numberCell::before {
  counter-increment: number-cell;
  content: counter(mumber-cell);
}
3 Likes

Thanks.

About the last part where you suggested doing the count with CSS.

In the browser console, replacing:

let baseNumber = 1;
document.querySelectorAll('.numberCell').forEach((element)=>{
    element.innerHTML = `<span>${baseNumber++}</span>`;
});

With:

newStyle.type = "text/css";
newStyle.innerHTML +=`
tbody {
  counter-reset: number-cell;
}

.numberCell::before {
  counter-increment: number-cell;
  content: counter(mumber-cell);
}
`;

document.head.appendChild(newStyle);

Didn’t output numbers.

So, I am not sure if you meant a frontend approach.

@bendqh1,

m3g4p0p is suggesting a purely CSS solution to the numbering. This is separate to your JS.

Here is hopefully a simple example.

2 Likes

Don’t you mean no js required at all :slight_smile:

Here it is styled a bit better :wink:

3 Likes

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