Javascript cut table to insert a tag

2019-09-19 12:14发布

问题:

I want to cut table after tr to insert a div and re-open the table :

Before :

<table>
    <tr onClick="cutAfter(this);">
        <td>bla</td><td> 123 </td><td>Yes </td>
    </tr>
    <tr onClick="cutAfter(this);">
        <td>bli</td><td> 456 </td><td>no</td>
    </tr>
    <tr onClick="cutAfter(this);">
        <td>blu</td><td> 789 </td><td>hum</td>
    </tr>
</table>

After :

<table>
    <tr onClick="cutAfter(this);">
        <td>bla</td><td> 123 </td><td>Yes </td>
    </tr>
    <tr onClick="cutAfter(this);">
        <td>bli</td><td> 456 </td><td>no</td>
    </tr>
</table>
<div onClick="fuse(this)">It works</div>
<table>
    <tr onClick="cutAfter(this);">
        <td>blu</td><td> 789 </td><td>hum</td>
    </tr>
</table>

And return to first state on click. Any idea (no jQuery).

回答1:

A simple whay to do it is to use combination of DOM manipulation methods like insertAdjacentHTML to create new table and appendChild to move rows into new table:

function cutAfter(row) {
    var table = row.parentNode.parentNode;
  
    if (row.nextElementSibling) {
        table.insertAdjacentHTML('afterend', '<table><tbody></tbody></table>');
        var newTable = table.nextElementSibling.tBodies[0];

        while (row.nextElementSibling) {
            newTable.appendChild(row.nextElementSibling);
        }
    }

}
table {
    margin-bottom: 10px;
}
table td {
    border: 1px #AAA solid;
}
<table>
    <tr onClick="cutAfter(this);">
        <td>bla</td><td> 123 </td><td>Yes </td>
    </tr>
    <tr onClick="cutAfter(this);">
        <td>bli</td><td> 456 </td><td>no</td>
    </tr>
    <tr onClick="cutAfter(this);">
        <td>blu</td><td> 789 </td><td>hum</td>
    </tr>
</table>



回答2:

Here's a simple example, made of an HTML file (the structure) and a Javascript file (the behavior). The script uses node manipulation, so as to preserve existing handlers (e.g. added by other scripts). It also attaches event handlers directly, using element.onxxx, to keep things simple, but you should replace that by your favorite event manager.

var makeSplittable = function(table, joinText) {

  init();

  function init() {
    var tBodies = table.tBodies;
    for (var ii = 0; ii < tBodies.length; ii++) {
      var rows = tBodies[ii].rows;
      for (var j = 0; j < rows.length; j++) {
        rows[j].onclick = split; // Replace with your favorite event manager
      }
    }
  }

  function split(evt) {
    var rowIndex = this.rowIndex;
    var tbody = findParent(this, "tbody");
    var numRows = tbody.rows.length;
    if (rowIndex < numRows - 1) {
      var rows = [];
      for (var ii = rowIndex + 1; ii < numRows; ii++) {
        rows.push(tbody.rows[ii]);
      }
      var existingTable = findParent(this, "table");
      var newTable = createTable(rows);
      var joiner = createJoiner();
      existingTable.parentNode.insertBefore(newTable, existingTable.nextSibling);
      existingTable.parentNode.insertBefore(joiner, existingTable.nextSibling);
    }
  }

  function createTable(rows) {
    var table = document.createElement("table");
    var tbody = document.createElement("tbody");
    for (var ii = 0; ii < rows.length; ii++) {
      tbody.appendChild(rows[ii]);
    }
    table.appendChild(tbody);
    return table;
  }

  function createJoiner() {
    var div = document.createElement("div");
    var content = document.createTextNode(joinText);
    div.appendChild(content);
    div.onclick = join; // same
    return div;
  }

  function join(evt) {
    var previousTable = this.previousSibling;
    var nextTable = this.nextSibling;
    var tbody = previousTable.tBodies[previousTable.tBodies.length - 1];
    var rows = nextTable.rows;
    while (rows.length) {
      tbody.appendChild(rows[0]);
    }
    nextTable.parentNode.removeChild(nextTable);
    this.parentNode.removeChild(this);
  }

  function findParent(element, type) {
    if (!element || !type) {
      return null;
    }
    if (element.nodeName.toLowerCase() == type.toLowerCase()) {
      return element;
    }
    return findParent(element.parentNode, type);
  }

};

makeSplittable(document.getElementById("target"), "Merge adjacent tables");
table,
div {
  margin: 5px 0;
}
tr:hover td {
  background-color: orange;
}
td {
  background-color: yellow;
  cursor: pointer;
  padding: 10px;
}
div {
  color: #0c0;
  cursor: pointer;
}
<table id="target">
  <tr>
    <td>bla</td>
    <td>123</td>
    <td>Yes</td>
  </tr>
  <tr>
    <td>bli</td>
    <td>456</td>
    <td>no</td>
  </tr>
  <tr>
    <td>blu</td>
    <td>789</td>
    <td>hum</td>
  </tr>
</table>



回答3:

A possibility, assuming that a DIV should not be inserted after the last TR if there has not been a cut, but it would have been nice to see your effort. Also assuming no <thead> or <tfoot>.

function isTagName(element, tagName) {
  return element.tagName.toUpperCase() === tagName.toUpperCase();
}

function getClosest(element, tagName) {
  var closest = null;

  while (element !== document && !isTagName(element, tagName)) {
    element = element.parentNode;
  }

  if (element !== document && isTagName(element, tagName)) {
    closest = element;
  }

  return closest;
}

function insertAfter(newNode, referenceNode) {
  return referenceNode.parentNode
          .insertBefore(newNode, referenceNode.nextSibling);
}

function moveAppend(list, dest, from) {
  var index = list.length - 1,
    last;

  for (last = from || 0; index >= last; index -= 1) {
    dest.appendChild(list[index]);
  }

  return dest;
}

document.body.addEventListener('click', function(e) {
  var target = e.target,
    tr = getClosest(target, 'tr'),
    newDiv,
    newTable,
    newBody,
    next,
    parent;

  if (tr) {
    if (tr.rowIndex < tr.parentNode.rows.length - 1) {
      newDiv = document.createElement('div');
      newDiv.appendChild(document.createTextNode('It works!'));
      insertAfter(newDiv, getClosest(tr, 'table'));
      newTable = document.createElement('table');
      newBody = document.createElement('tbody');
      moveAppend(tr.parentNode.rows, newBody, tr.rowIndex + 1);
      newTable.appendChild(newBody);
      insertAfter(newTable, newDiv);
    }
  } else if (isTagName(target, 'div') &&
              isTagName(target.previousElementSibling, 'table') &&
              isTagName(target.nextElementSibling, 'table')) {

    next = target.nextElementSibling;
    moveAppend(next.tBodies[0].rows, target.previousElementSibling.tBodies[0]);
    parent = target.parentNode;
    parent.removeChild(next);
    parent.removeChild(target);
  }
}, false);
table,
td {
  border-style: solid;
  border-width: 1px;
}
div {
  background-color: yellow;
}
<table>
  <tr>
    <td>bla</td>
    <td>123</td>
    <td>Yes</td>
  </tr>
  <tr>
    <td>bli</td>
    <td>456</td>
    <td>no</td>
  </tr>
  <tr>
    <td>blu</td>
    <td>789</td>
    <td>hum</td>
  </tr>
</table>