使用可扩展部分对表数据进行排序

时间:2018-11-23 21:41:45

标签: javascript jquery html

现在我有这个:

<style>
  table {
      border-spacing: 0;
      width: 100%;
      border: 1px solid #ddd;
  }

  th, td {
      text-align: left;
      padding: 16px;
  }

  tr:nth-child(even) {
      background-color: #f2f2f2
  }
</style>
<table id="schInfoTable">
  <thead>
    <th onclick="sortTable(0)">Date</th>
    <th>Amount</th>
    <th>Count</th>
  </thead>
  <tbody>
    <tr>
      <td><a onclick="openView('2018-11-14')">2018-11-14</a></td>
      <td>$23,000.00</td>
      <td>12</td>
    </tr>
    <tr style="display:none; background-color: #cbe7cb;" class="2018-11-14">
      <td>Mandy</td>
      <td>Designer</td>
      <td>View</td>
    </tr>
    <tr style="display:none; background-color: #cbe7cb;" class="2018-11-14">
      <td>Robert</td>
      <td>Cook</td>
      <td>View</td>
    </tr>
    <tr>
      <td><a onclick="openView('2018-11-13')">2018-11-13</a></td>
      <td>$13,000.00</td>
      <td>8</td>
    </tr>
    <tr style="display:none; background-color: #cbe7cb;" class="2018-11-13 branches">
      <td>James</td>
      <td>Driver</td>
      <td>View</td>
    </tr>
  </tbody>
</table>
<script>

  function openView(showID){
        $("."+showID).toggle();
    }

  function sortTable(n) {

      var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
      table = document.getElementById("schInfoTable");
      switching = true;
      //Set the sorting direction to ascending:
      dir = "asc"; 
      /*Make a loop that will continue until
      no switching has been done:*/
      while (switching) {
        //start by saying: no switching is done:
        switching = false;
        rows = table.rows;
        /*Loop through all table rows (except the
        first, which contains table headers):*/
        for (i = 1; i < (rows.length - 1); i++) {
          //start by saying there should be no switching:
          shouldSwitch = false;
          /*Get the two elements you want to compare,
          one from current row and one from the next:*/
          x = rows[i].getElementsByTagName("TD")[n];
          y = rows[i + 1].getElementsByTagName("TD")[n];
          /*check if the two rows should switch place,
          based on the direction, asc or desc:*/
          if (dir == "asc") {
            if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
              //if so, mark as a switch and break the loop:
              shouldSwitch= true;
              break;
            }
          } else if (dir == "desc") {
            if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
              //if so, mark as a switch and break the loop:
              shouldSwitch = true;
              break;
            }
          }
        }
        if (shouldSwitch) {
          /*If a switch has been marked, make the switch
          and mark that a switch has been done:*/
          rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
          switching = true;
          //Each time a switch is done, increase this count by 1:
          switchcount ++;      
        } else {
          /*If no switching has been done AND the direction is "asc",
          set the direction to "desc" and run the while loop again.*/
          if (switchcount == 0 && dir == "asc") {
            dir = "desc";
            switching = true;
          }
        }
      }
    }
</script>

jsfiddle代码在这里:jsfiddle

点击日期后,它将展开并显示带有数据的其他表格行:enter image description here

但是,当您单击“日期”标题以按日期排序时,一切都会出错:

enter image description here

如您所见,绿色突出显示的表格数据一起放在底部,但是它们应该像这样:

enter image description here

我该如何实现?

更新:由于使用了David784的代码,因此我能够获得理想的结果,尽管不得不多花了t周的时间,但一切都归功于David。这是我现在拥有的:

function openView(showID) {
  $("." + showID).toggle();
}

function sortTable(n) {

  var table, rows, i, x, y = 0;
  var compare1, compare2;
  table = document.getElementById("schInfoTable");
  switching = true;

  rows = table.querySelectorAll('tr.sort');

  var sortArr = [];
  for (i = 0; i < rows.length; i++) {
    x = rows[i];
    if (i + 1 in rows) y = rows[i + 1].previousElementSibling;
    else y = x.parentElement.lastChild;
    var obj = {
      sort: x.getElementsByTagName("TD")[n].textContent.toLowerCase(),
      range: document.createRange()
    };
    obj.range.setStartBefore(x);
    obj.range.setEndAfter(y);
    sortArr.push(obj);
  }
  function fnSortArrAsc(a, b) {
    if (a.sort > b.sort) return 1;
    else if (a.sort < b.sort) return -1;
    else return 0;
  }
  function fnSortArrDesc(a, b) {
    if (a.sort < b.sort) return 1;
    else if (a.sort > b.sort) return -1;
    else return 0;
  }

  compare1 = rows[0].getElementsByTagName("TD")[0].textContent.toLowerCase();
  compare2 = rows[rows.length-1].getElementsByTagName("TD")[0].textContent.toLowerCase();
  if(compare1 < compare2){
    sortArr = sortArr.sort(fnSortArrDesc);
  }else{
    sortArr = sortArr.sort(fnSortArrAsc);
  }

  frag = document.createDocumentFragment();
  for (i = 0; i < sortArr.length; i++) {
    x = sortArr[i];
    frag.appendChild(x.range.extractContents());
  }
  table.appendChild(frag);
}

完整的工作代码在这里:jsfiddle

2 个答案:

答案 0 :(得分:2)

如果要保持表结构几乎相同,这将是完成您要尝试执行的操作的一种方法。

JavaScript的简要说明:

假设:

  • 在每个“顶级” TR上添加类。这些是要排序的行
  • 任何非顶级TR均假定为位于其上方的顶级TR的子行,并将与该行一起移动。

方法

  • 使用该新类在querySelectorAll中添加到顶级TR中,以获取我们要排序的所有内容的列表。
  • 循环:创建对象数组,包括
    • 排序值(正确TD的小写字符串内容)
    • 行的DOM范围及其下的所有子行
  • 然后使用具有简单自定义排序功能的内置javascript Array.sort
  • 循环:按顺序将所有范围内容提取到documentFragment
  • 将documentFragment追加到表中

我使用documentFragment的原因是,与每次将每个范围一次直接添加回表元素相比,它here on MDN节省了DOM回流和渲染的费用。

注意::如果您有表格页脚,则可能想利用tbody元素,而不是直接处理表格。

    function openView(showID) {
      $("." + showID).toggle();
    }

    function sortTable(n) {

      var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
      table = document.getElementById("schInfoTable");
      switching = true;
      //Set the sorting direction
      dir = 1;
      var thEl = table.querySelectorAll('th')[n];
      if (thEl.classList.contains('asc')) dir = -1;
      thEl.classList.toggle('asc');

      rows = table.querySelectorAll('tr.sort');
      var sortArr = [];
      for (i = 0; i < rows.length; i++) {
        x = rows[i];
        if (i + 1 in rows) y = rows[i + 1].previousElementSibling;
        else y = x.parentElement.lastChild;
        var obj = {
          sort: x.getElementsByTagName("TD")[n].textContent.toLowerCase(),
          range: document.createRange()
        };
        obj.range.setStartBefore(x);
        obj.range.setEndAfter(y);
        sortArr.push(obj);
      }
      function fnSortArr(a, b) {
        if (a.sort > b.sort) return 1 * dir;
        else if (a.sort < b.sort) return -1 * dir;
        else return 0;
      }
      sortArr = sortArr.sort(fnSortArr);
      console.log(JSON.stringify(sortArr, null, 2));
      frag = document.createDocumentFragment();
      for (i = 0; i < sortArr.length; i++) {
        x = sortArr[i];
        frag.appendChild(x.range.extractContents());
      }
      table.appendChild(frag);
    }
    table {
      border-spacing: 0;
      width: 100%;
      border: 1px solid #ddd;
    }

    th,
    td {
      text-align: left;
      padding: 16px;
    }

    tr:nth-child(even) {
      background-color: #f2f2f2
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="schInfoTable">
    <thead>
      <th onclick="sortTable(0)">Date</th>
      <th>Amount</th>
      <th>Count</th>
    </thead>
    <tbody>
      <tr class='sort'>
        <td><a onclick="openView('2018-11-14')">2018-11-14</a></td>
        <td>$23,000.00</td>
        <td>12</td>
      </tr>
      <tr style="display:none; background-color: #cbe7cb;" class="2018-11-14">
        <td>Mandy</td>
        <td>Designer</td>
        <td>View</td>
      </tr>
      <tr style="display:none; background-color: #cbe7cb;" class="2018-11-14">
        <td>Robert</td>
        <td>Cook</td>
        <td>View</td>
      </tr>
      <tr class='sort'>
        <td><a onclick="openView('2018-11-13')">2018-11-13</a></td>
        <td>$13,000.00</td>
        <td>8</td>
      </tr>
      <tr style="display:none; background-color: #cbe7cb;" class="2018-11-13 branches">
        <td>James</td>
        <td>Driver</td>
        <td>View</td>
      </tr>
    </tbody>
  </table>

*编辑:添加排序顺序切换

通常使用的另一种方法是将子行放入子表中。例如,您的子数据如下所示: <tr><td colspan='3'> <table>...</table> </td></tr>。 然后,每个顶层行下面都有一个子表行,您总是成对地对行进行排序/移动,而不是处理从零到无穷大的任意数量的子行。

答案 1 :(得分:1)

我对您的问题非常了解,并提出了我的想法。 我希望这个方法能对您有所帮助。

let testData = [{
    date: "2018-11-14",
    amount: 23000,
    count: 12,
    people: [{
        name: "Mandy",
        designation: "Designer",
        detail: "View",
      },
      {
        name: "Robert",
        designation: "Cook",
        detail: "View",
      }
    ]
  },
  {
    date: "2018-11-13",
    amount: 13000,
    count: 8,
    people: [{
      name: "James",
      designation: "Driver",
      detail: "View",
    }]
  }
]

let testDiv = document.querySelector('#test');

let main = function() {
  let mainTable = document.createElement('table');
  mainTable.classList.add('table-style');
  let mainTbody = document.createElement('tbody');
  let mainTheader = document.createElement('tr');
  mainTheader.onclick = sort;
  let dateHeader = document.createElement('th');
  dateHeader.textContent = 'Date';
  mainTheader.appendChild(dateHeader);
  let amountHeader = document.createElement('th');
  mainTheader.appendChild(amountHeader);
  amountHeader.textContent = 'Amount';
  let countHeader = document.createElement('th');
  countHeader.textContent = 'Count';
  mainTheader.appendChild(countHeader);
  mainTable.appendChild(mainTheader);


  let counter = 0;
  testData.forEach(object => {
    console.log('MAIN', object);

    counter++;
    let rowEl = document.createElement('tr');
    if (counter % 2 === 0) {
      rowEl.classList.add('uneven-row');
    }
    rowEl.onclick = toggle;
    rowEl.id = object.date;

    let keys = Object.keys(object);
    keys.forEach(key => {
      console.log(key, ": ", object[key]);

      if (key !== 'people') {
        let colEl = document.createElement('td');
        colEl.textContent = object[key];
        rowEl.appendChild(colEl);

      } else {
        mainTbody.appendChild(rowEl);
        let subTableRow = document.createElement('tr');
        subTableRow.id = 'detail-' + object.date;
        subTableRow.classList.add('hidden');

        let subTable = document.createElement('table');
        subTable.classList.add('sub-table-style');
        subTableRow.appendChild(subTable);

        let subTbody = document.createElement('tbody');

        object[key].forEach(detail => {
          console.log('DETAIL', detail);

          let subRowEl = document.createElement('tr');

          let detailKeys = Object.keys(detail);
          detailKeys.forEach(detailKey => {
            console.log(detailKey, ": ", detail[detailKey]);

            let subColEl = document.createElement('td');
            subColEl.textContent = detail[detailKey];
            subRowEl.appendChild(subColEl);
          });
          subTbody.appendChild(subRowEl);
        });
        subTable.appendChild(subTbody);
        mainTbody.appendChild(subTableRow);
      }
    });
  });
  mainTable.appendChild(mainTbody);
  testDiv.appendChild(mainTable);
}

let toggle = function() {
  let detailEl = document.querySelector('#detail-' + this.id);
  console.log('TOGGLE', detailEl);

  if (detailEl.classList.contains('hidden')) {
    detailEl.classList.toggle('hidden');
    console.log('SHOW', detailEl.id);
  } else {
    detailEl.classList.toggle('hidden');
    console.log('HIDE', detailEl);
  }
}

let compareAsc = function(a, b) {
  console.log('A', a);
  console.log('B', b);
  if (a.date > b.date) {
    return 1;
  }
  if (a.date < b.date) {
    return -1;
  }
  if (a.date === b.date) {
    return 0;
  }
}

let compareDesc = function(a, b) {
  console.log('A', a);
  console.log('B', b);
  if (a.date < b.date) {
    return 1;
  }
  if (a.date > b.date) {
    return -1;
  }
  if (a.date === b.date) {
    return 0;
  }
}

let redraw = function() {
  while (testDiv.firstChild) {
    testDiv.removeChild(testDiv.firstChild);
  }

  main();
}

let sort = function() {
  console.log('SORT', this.classList)
  if (!window.sorted) {
    console.log('SORT ASC', this);
    testData.sort(compareAsc);
    window.sorted = true;

    redraw();
  } else {
    console.log('SORT DESC', this);
    testData.sort(compareDesc);
    window.sorted = false;

    redraw();
  }
}

main();
body {
  font-family: 'Courier New', Courier, monospace;
}

table {
  border: 1px solid #ccc;
  border-collapse: collapse;
  table-layout: fixed;
  width: 600px;
}

.sub-table-style {
  border: 0px;
  background: lawngreen;
}

td {
  width: 150px;
  padding: 10px;
  margin: 0;
  border: 0px;
}

.uneven-row {
  background: #ddd;
}

.hidden {
  visibility: hidden;
  display: none;
  height: 0px;
  overflow: hidden;
}
<div id="test">

</div>