Removing dynamic rowspan lines in table

Asked

Viewed 64 times

2

I have an element <table> to which I insert lines with <td> whose rowspan varies according to the value entered in input. I would like to remove lines at the click of the button Removes Rows in accordance with the radioButton selected.

Follow the code working:

function addLines(){
  tabBody=document.getElementById("myTable");
  qtdRowsInput = document.getElementById("qtdRows").value;

  var valueRadioButton = 1;
  for (var i = 0; i < qtdRowsInput; i++) {
    row=document.createElement("tr");
           
    if(i == 0){
      var tdElement0 = document.createElement("td");
      tdElement0.rowSpan = qtdRowsInput;
    }

    var tdElement1 = document.createElement("td");
    var tdElement2 = document.createElement("td");
    var checkbox = document.createElement('input');
    checkbox.type = "radio";
    checkbox.name = "radioButton";
    checkbox.value = valueRadioButton;
    checkbox.id = "rdb"+valueRadioButton;

    textnode1=document.createTextNode(qtdRowsInput);  
    textnode2=document.createTextNode("A");

    if(i == 0){
      tdElement0.appendChild(checkbox);
    }

    tdElement1.appendChild(textnode1);
    tdElement2.appendChild(textnode2);

    if(i == 0){
      row.appendChild(tdElement0);
    }
    row.appendChild(tdElement1);
    row.appendChild(tdElement2);
    tabBody.appendChild(row);
  }

  valueRadioButton++;
}

function removeRows(){

}
table, th, td {
  border: 1px solid black;
  border-collapse: collapse;
}
Rows: <input type="number" id="qtdRows" min="1"/> <button onclick="addLines();">Add rows</button> <button onclick="removeRows();">Remove rows</button>  <br> <br>

<table id="myTable">
  <tr>
    <th>X</th>
    <th>Numbers</th>
    <th>Letters</th>
  </tr>
</table>

  • 1

    Downvoter there’s something wrong with the question?

2 answers

1

To do this, you need to perform 3 steps:

  • Identify the selected radio
  • Get indices for the parent element of each row
  • Remove the lines

The code would stand:

var radio = document.querySelector('[name=radioButton]:checked');
if (radio) {
  var cell = radio.parentElement;
  var row = cell.parentElement;
  var tableChildren = row.parentElement.children;
  var index = Array.from(tableChildren).indexOf(row);
  var limit = index + parseInt(cell.rowSpan);
  while (limit-- > index) {
    tableChildren[limit].remove()
  }
}

Check on snippet down with the working code:

function addLines(){
  tabBody=document.getElementById("myTable");
  qtdRowsInput = document.getElementById("qtdRows").value;

  var valueRadioButton = 1;
  for (var i = 0; i < qtdRowsInput; i++) {
    row=document.createElement("tr");
             
    if(i == 0){
      var tdElement0 = document.createElement("td");
      tdElement0.rowSpan = qtdRowsInput;
    }

    var tdElement1 = document.createElement("td");
    var tdElement2 = document.createElement("td");
    var checkbox = document.createElement('input');
    checkbox.type = "radio";
    checkbox.name = "radioButton";
    checkbox.value = valueRadioButton;
    checkbox.id = "rdb"+valueRadioButton;

    textnode1=document.createTextNode(qtdRowsInput);  
    textnode2=document.createTextNode("A");

    if(i == 0){
      tdElement0.appendChild(checkbox);
    }

    tdElement1.appendChild(textnode1);
    tdElement2.appendChild(textnode2);

    if(i == 0){
      row.appendChild(tdElement0);
    }
    row.appendChild(tdElement1);
    row.appendChild(tdElement2);
    tabBody.appendChild(row);
  }
  valueRadioButton++;
}

function removeRows(){
  var radio = document.querySelector('[name=radioButton]:checked');
  if (radio) {
    var cell = radio.parentElement;
    var row = cell.parentElement;
    var tableChildren = row.parentElement.children;
    var index = Array.from(tableChildren).indexOf(row);
    var limit = index + parseInt(cell.rowSpan);
    while (limit-- > index) {
      tableChildren[limit].remove()
    }
  }
}
table, th, td {
  border: 1px solid black;
  border-collapse: collapse;
}
Rows: <input type="number" id="qtdRows" min="1" value="1"/>
<button onclick="addLines();">Add rows</button>
<button onclick="removeRows();">Remove rows</button>
<br><br>
<table id="myTable">
  <tr>
    <th>X</th>
    <th>Numbers</th>
    <th>Letters</th>
  </tr>
</table>

  • I hardly ever see the use of indexOf comparing objects by their own reference. Cool! + 1 :) The only caveat I can think of is that it can be a little (almost nothing) less efficient depending on the size of the list of elements.

1

Once the user clicks on the remove button the selected lines, some steps need to be performed.

First, we want to get the element that the user selected. For this, we do something like:

const input = document.querySelector('#myTable [name="radioButton"]:checked');

This will return within the selectable limits of #myTable, the field whose name be equal to radioButton and that was marked by the user.

Having selected the element, we need to determine the rowspan related to it. In possession of the rowspan numeric value N, just select the N - 1 lines after the current one (including the current one).

Then just remove all the selected elements.

Put it all together, we’ll have something like:

function removeRows() {
  const input = document.querySelector('#myTable [name="radioButton"]:checked');

  // Garante que o código abaixo não seja executado caso nenhum elemento seja selecionado:
  if (!input) return;

  const td = input.parentElement;
  const { rowSpan } = td; // A célula possui atributo numérico `rowSpan`.

  // Ascendemos mais uma vez na árvore para obter a linha atual:
  const tr = td.parentElement;

  // Obtemos a lista das linhas a serem removidas:
  const rowsToRemove = getNextNElements(tr, rowSpan - 1);

  for (const row of rowsToRemove) {
    row.remove();
  }
}

Note that the function getNextNElements is not native browsers. Therefore, we need to implement it in some way. A possibility would be something like:

/**
 * Seleciona o elemento e os próximos N.
 *
 * @param {HTMLElement} current
 * @param {number} n
 * @return {Array<HTMLElement>}
 */
function getNextNElements(current, n) {
  const list = [current];

  while (n-- > 0) {
    const last = list[list.length - 1];
    list.push(last?.nextElementSibling ?? null);
  }

  return list;
}

It basically takes an N child of a given element and creates a list of the current and next element n (passed via 2nd argument). The advantage of using this type of function is that it is not necessary to scan the entire list of elements (which can be extensive) to determine the first element to be removed.

The optional chaining up ?. and null coalescence operator were used if n is greater than the number of remaining elements. This prevents errors related to the attempt to access some property of null. Learn more here.

We’ll finally have something like this:

function addLines() {
  tabBody = document.getElementById('myTable');
  qtdRowsInput = document.getElementById('qtdRows').value;

  var valueRadioButton = 1;
  for (var i = 0; i < qtdRowsInput; i++) {
    row = document.createElement('tr');

    if (i == 0) {
      var tdElement0 = document.createElement('td');
      tdElement0.rowSpan = qtdRowsInput;
    }

    var tdElement1 = document.createElement('td');
    var tdElement2 = document.createElement('td');
    var checkbox = document.createElement('input');
    checkbox.type = 'radio';
    checkbox.name = 'radioButton';
    checkbox.value = valueRadioButton;
    checkbox.id = 'rdb' + valueRadioButton;

    textnode1 = document.createTextNode(qtdRowsInput);
    textnode2 = document.createTextNode('A');

    if (i == 0) {
      tdElement0.appendChild(checkbox);
    }

    tdElement1.appendChild(textnode1);
    tdElement2.appendChild(textnode2);

    if (i == 0) {
      row.appendChild(tdElement0);
    }
    row.appendChild(tdElement1);
    row.appendChild(tdElement2);
    tabBody.appendChild(row);
  }

  valueRadioButton++;
}

function removeRows() {
  const input = document.querySelector('#myTable [name="radioButton"]:checked');

  // Garante que o código abaixo não seja executado caso nenhum elemento seja selecionado:
  if (!input) return;

  const td = input.parentElement;
  const { rowSpan } = td; // A célula possui atributo numérico `rowSpan`.

  // Ascendemos mais uma vez na árvore para obter a linha atual:
  const tr = td.parentElement;

  // Obtemos a lista das linhas a serem removidas:
  const rowsToRemove = getNextNElements(tr, rowSpan - 1);

  for (const row of rowsToRemove) {
    row.remove();
  }
}

/**
 * Seleciona o elemento e os próximos N.
 *
 * @param {HTMLElement} current
 * @param {number} n
 * @return {Array<HTMLElement>}
 */
function getNextNElements(current, n) {
  const list = [current];

  while (n-- > 0) {
    const last = list[list.length - 1];
    list.push(last?.nextElementSibling ?? null);
  }

  return list;
}
table, th, td {
  border: 1px solid black;
  border-collapse: collapse;
}
Rows: <input type="number" id="qtdRows" min="1"/> <button onclick="addLines();">Add rows</button> <button onclick="removeRows();">Remove rows</button>  <br> <br>

<table id="myTable">
  <tr>
    <th> X </th>
    <th> Numbers</th>
    <th> Letters</th>
  </tr>
</table>

Hidden so as not to occupy too much space unnecessarily.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.