logo

blog

My website can't be that messy, right? git clone https://anongit.hacktivis.me/git/blog.git/

sorttable.js (4074B)


  1. /*
  2. * Copyright 2007 Stuart Langridge http://www.kryogenix.org/code/browser/sorttable/
  3. * Copyright 2018, 2023, 2025 Haelwenn (lanodan) Monnier <contact@hacktivis.me>
  4. * SPDX-License-Identifier: X11
  5. */
  6. /* Usage:
  7. * Add <script src="sorttable.js"></script> to your (x)HTML pages
  8. * Add class="sortable" to any table you'd like to make sortable
  9. * See sorttable.css for an example of styling
  10. */
  11. sorttable = {
  12. done : false
  13. };
  14. sorttable.init = function() {
  15. // quit if this function has already been called
  16. if(sorttable.done) return;
  17. // flag this function so we don't do the same thing twice
  18. sorttable.done = true;
  19. document.querySelectorAll("table.sortable").forEach((e) => {
  20. sorttable.makeSortable(e);
  21. });
  22. };
  23. sorttable.makeSortable = function(table) {
  24. // Only one thead row is supported
  25. if(table.tHead.rows.length != 1) return;
  26. // work through each column and calculate its type
  27. headrow = table.tHead.rows[0].cells;
  28. for(var i = 0; i < headrow.length; i++)
  29. {
  30. // skip this col
  31. if(!headrow[i].classList.contains("sorttable_nosort"))
  32. {
  33. // make it clickable to sort
  34. headrow[i].sorttable_columnindex = i;
  35. headrow[i].sorttable_tbody = table.tBodies[0];
  36. headrow[i].addEventListener("click", event => {
  37. if(event.target)
  38. {
  39. sorttable.innerSortFunction(event.target)
  40. }
  41. });
  42. headrow[i].addEventListener("keydown", event => {
  43. if(event.target && (event.key && event.key == "Enter"))
  44. {
  45. sorttable.innerSortFunction(event.target);
  46. }
  47. });
  48. headrow[i].setAttribute("tabindex", "0");
  49. headrow[i].classList.add('made_sortable');
  50. }
  51. }
  52. };
  53. sorttable.compareFn = function(a, b) {
  54. // Note: Returns are reversed
  55. if(a[0]<b[0]) {return 1};
  56. if(a[0]>b[0]) {return -1};
  57. return 0;
  58. };
  59. sorttable.innerSortFunction = function(tr) {
  60. if(tr.classList.contains("sorttable_sorted"))
  61. {
  62. sorttable.reverse(tr.sorttable_tbody);
  63. tr.classList.replace('sorttable_sorted', 'sorttable_sorted_reverse');
  64. }
  65. else if(tr.classList.contains("sorttable_sorted_reverse"))
  66. {
  67. sorttable.reverse(tr.sorttable_tbody);
  68. tr.classList.replace('sorttable_sorted_reverse', 'sorttable_sorted');
  69. }
  70. else
  71. {
  72. // remove sorttable_sorted classes
  73. theadRow = tr.parentNode;
  74. theadRow.childNodes.forEach(function(cell) {
  75. cell.classList.remove('sorttable_sorted_reverse', 'sorttable_sorted');
  76. });
  77. // build an array to sort. This is a Schwartzian transform thing,
  78. // i.e., we "decorate" each row with the actual sort key,
  79. // sort based on the sort keys, and then put the rows back in order
  80. // which is a lot faster because you only do getInnerText once per row
  81. row_array = [];
  82. col = tr.sorttable_columnindex;
  83. rows = tr.sorttable_tbody.rows;
  84. for(var j = 0; j < rows.length; j++)
  85. {
  86. row_array[row_array.length] =
  87. [ sorttable.getElementValue(rows[j].cells[col]), rows[j] ];
  88. }
  89. row_array.sort(sorttable.compareFn);
  90. tb = tr.sorttable_tbody;
  91. row_array.forEach(function(row) { tb.appendChild(row[1]); });
  92. tr.classList.add('sorttable_sorted');
  93. }
  94. };
  95. // - Use data-value="" attribute if you want to override innerHTML
  96. // - Use data-type="" attribute if you want to use non-lexical sorting
  97. // Example: <td data-value="1337" data-type="int">Leet</td>
  98. sorttable.getElementValue = function(element) {
  99. if(element === undefined)
  100. {
  101. return "\u0000"; // null
  102. console.log("Warning: Empty cells found");
  103. };
  104. let value = element.dataset.value ? element.dataset.value : element.innerText;
  105. switch(element.dataset.type)
  106. {
  107. case undefined:
  108. return value;
  109. case "int":
  110. var v = parseInt(value);
  111. return v != NaN ? v : value;
  112. case "float":
  113. var v = parseFloat(value);
  114. return v != NaN ? v : value;
  115. default:
  116. console.log(`Warning: Unhandled type ${element.dataset.type}`);
  117. return value;
  118. };
  119. };
  120. sorttable.reverse = function(tbody) {
  121. // reverse the rows in a tbody
  122. newrows = [];
  123. for(var i = 0; i < tbody.rows.length; i++)
  124. {
  125. newrows[newrows.length] = tbody.rows[i];
  126. }
  127. for(var i = newrows.length - 1; i >= 0; i--)
  128. {
  129. tbody.appendChild(newrows[i]);
  130. }
  131. delete newrows;
  132. };
  133. sorttable.init()