jquery.filterTable

This plugin will add a search filter to tables. When typing in the filter, any rows that do not contain the filter will be hidden.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/423931/914633/jqueryfilterTable.js

  1. /**
  2. * jquery.filterTable
  3. *
  4. * This plugin will add a search filter to tables. When typing in the filter,
  5. * any rows that do not contain the filter will be hidden.
  6. *
  7. * Utilizes bindWithDelay() if available. https://github.com/bgrins/bindWithDelay
  8. *
  9. * @version v1.5.7
  10. * @author Sunny Walker, swalker@hawaii.edu
  11. * @license MIT
  12. */
  13. (function ($) {
  14. var jversion = $.fn.jquery.split('.'),
  15. jmajor = parseFloat(jversion[0]),
  16. jminor = parseFloat(jversion[1]);
  17. // build the pseudo selector for jQuery < 1.8
  18. if (jmajor < 2 && jminor < 8) {
  19. // build the case insensitive filtering functionality as a pseudo-selector expression
  20. $.expr[':'].filterTableFind = function (a, i, m) {
  21. return $(a).text().toUpperCase().indexOf(m[3].toUpperCase().replace(/"""/g, '"').replace(/"\\"/g, "\\")) >= 0;
  22. };
  23. // build the case insensitive all-words filtering functionality as a pseudo-selector expression
  24. $.expr[':'].filterTableFindAny = function (a, i, m) {
  25. // build an array of each non-falsey value passed
  26. var raw_args = m[3].split(/[\s,]/),
  27. args = [];
  28. $.each(raw_args, function (j, v) {
  29. var t = v.replace(/^\s+|\s$/g, '');
  30. if (t) {
  31. args.push(t);
  32. }
  33. });
  34. // if there aren't any non-falsey values to search for, abort
  35. if (!args.length) {
  36. return false;
  37. }
  38. return function (a) {
  39. var found = false;
  40. $.each(args, function (j, v) {
  41. if ($(a).text().toUpperCase().indexOf(v.toUpperCase().replace(/"""/g, '"').replace(/"\\"/g, "\\")) >= 0) {
  42. found = true;
  43. return false;
  44. }
  45. });
  46. return found;
  47. };
  48. };
  49. // build the case insensitive all-words filtering functionality as a pseudo-selector expression
  50. $.expr[':'].filterTableFindAll = function (a, i, m) {
  51. // build an array of each non-falsey value passed
  52. var raw_args = m[3].split(/[\s,]/),
  53. args = [];
  54. $.each(raw_args, function (j, v) {
  55. var t = v.replace(/^\s+|\s$/g, '');
  56. if (t) {
  57. args.push(t);
  58. }
  59. });
  60. // if there aren't any non-falsey values to search for, abort
  61. if (!args.length) {
  62. return false;
  63. }
  64. return function (a) {
  65. // how many terms were found?
  66. var found = 0;
  67. $.each(args, function (j, v) {
  68. if ($(a).text().toUpperCase().indexOf(v.toUpperCase().replace(/"""/g, '"').replace(/"\\"/g, "\\")) >= 0) {
  69. // found another term
  70. found++;
  71. }
  72. });
  73. return found === args.length; // did we find all of them in this cell?
  74. };
  75. };
  76. } else {
  77. // build the pseudo selector for jQuery >= 1.8
  78. $.expr[':'].filterTableFind = jQuery.expr.createPseudo(function (arg) {
  79. return function (el) {
  80. return $(el).text().toUpperCase().indexOf(arg.toUpperCase().replace(/"""/g, '"').replace(/"\\"/g, "\\")) >= 0;
  81. };
  82. });
  83. $.expr[':'].filterTableFindAny = jQuery.expr.createPseudo(function (arg) {
  84. // build an array of each non-falsey value passed
  85. var raw_args = arg.split(/[\s,]/),
  86. args = [];
  87. $.each(raw_args, function (i, v) {
  88. // trim the string
  89. var t = v.replace(/^\s+|\s$/g, '');
  90. if (t) {
  91. args.push(t);
  92. }
  93. });
  94. // if there aren't any non-falsey values to search for, abort
  95. if (!args.length) {
  96. return false;
  97. }
  98. return function (el) {
  99. var found = false;
  100. $.each(args, function (i, v) {
  101. if ($(el).text().toUpperCase().indexOf(v.toUpperCase().replace(/"""/g, '"').replace(/"\\"/g, "\\")) >= 0) {
  102. found = true;
  103. // short-circuit the searching since this cell has one of the terms
  104. return false;
  105. }
  106. });
  107. return found;
  108. };
  109. });
  110. $.expr[':'].filterTableFindAll = jQuery.expr.createPseudo(function (arg) {
  111. // build an array of each non-falsey value passed
  112. var raw_args = arg.split(/[\s,]/),
  113. args = [];
  114. $.each(raw_args, function (i, v) {
  115. // trim the string
  116. var t = v.replace(/^\s+|\s$/g, '');
  117. if (t) {
  118. args.push(t);
  119. }
  120. });
  121. // if there aren't any non-falsey values to search for, abort
  122. if (!args.length) {
  123. return false;
  124. }
  125. return function (el) {
  126. // how many terms were found?
  127. var found = 0;
  128. $.each(args, function (i, v) {
  129. if ($(el).text().toUpperCase().indexOf(v.toUpperCase().replace(/"""/g, '"').replace(/"\\"/g, "\\")) >= 0) {
  130. // found another term
  131. found++;
  132. }
  133. });
  134. // did we find all of them in this cell?
  135. return found === args.length;
  136. };
  137. });
  138. }
  139. // define the filterTable plugin
  140. $.fn.filterTable = function (options) {
  141. // start off with some default settings
  142. var defaults = {
  143. // make the filter input field autofocused (not recommended for accessibility)
  144. autofocus: false,
  145.  
  146. // callback function: function (term, table){}
  147. callback: null,
  148.  
  149. // class to apply to the container
  150. containerClass: 'filter-table',
  151.  
  152. // tag name of the container
  153. containerTag: 'p',
  154.  
  155. // jQuery expression method to use for filtering
  156. filterExpression: 'filterTableFind',
  157.  
  158. // if true, the table's tfoot(s) will be hidden when the table is filtered
  159. hideTFootOnFilter: false,
  160.  
  161. // class applied to cells containing the filter term
  162. highlightClass: 'alt',
  163.  
  164. // don't filter the contents of cells with this class
  165. ignoreClass: '',
  166.  
  167. // don't filter the contents of these columns
  168. ignoreColumns: [],
  169.  
  170. // use the element with this selector for the filter input field instead of creating one
  171. inputSelector: null,
  172.  
  173. // name of filter input field
  174. inputName: '',
  175.  
  176. // tag name of the filter input tag
  177. inputType: 'search',
  178.  
  179. // text to precede the filter input tag
  180. label: 'Filter:',
  181.  
  182. // filter only when at least this number of characters are in the filter input field
  183. minChars: 1,
  184.  
  185. // don't show the filter on tables with at least this number of rows
  186. minRows: 8,
  187.  
  188. // HTML5 placeholder text for the filter field
  189. placeholder: 'search this table',
  190.  
  191. // prevent the return key in the filter input field from trigger form submits
  192. preventReturnKey: true,
  193.  
  194. // list of phrases to quick fill the search
  195. quickList: [],
  196.  
  197. // class of each quick list item
  198. quickListClass: 'quick',
  199.  
  200. // quick list item label to clear the filter (e.g., '&times; Clear filter')
  201. quickListClear: '',
  202.  
  203. // tag surrounding quick list items (e.g., ul)
  204. quickListGroupTag: '',
  205.  
  206. // tag type of each quick list item (e.g., a or li)
  207. quickListTag: 'a',
  208.  
  209. // class applied to visible rows
  210. visibleClass: 'visible'
  211. },
  212. // mimic PHP's htmlspecialchars() function
  213. hsc = function (text) {
  214. return text.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  215. },
  216. // merge the user's settings into the defaults
  217. settings = $.extend({}, defaults, options);
  218.  
  219. // handle the actual table filtering
  220. var doFiltering = function (table, q) {
  221. // cache the tbody element
  222. var tbody = table.find('tbody');
  223. // if the filtering query is blank or the number of chars is less than the minChars option
  224. if (q === '' || q.length < settings.minChars) {
  225. // show all rows
  226. tbody.find('tr').show().addClass(settings.visibleClass);
  227. // remove the row highlight from all cells
  228. tbody.find('td').removeClass(settings.highlightClass);
  229. // show footer if the setting was specified
  230. if (settings.hideTFootOnFilter) {
  231. table.find('tfoot').show();
  232. }
  233. } else {
  234. // if the filter query is not blank
  235. var all_tds = tbody.find('td');
  236. // hide all rows, assuming none were found
  237. tbody.find('tr').hide().removeClass(settings.visibleClass);
  238. // remove previous highlights
  239. all_tds.removeClass(settings.highlightClass);
  240. // hide footer if the setting was specified
  241. if (settings.hideTFootOnFilter) {
  242. table.find('tfoot').hide();
  243. }
  244. if (settings.ignoreColumns.length) {
  245. var tds = [];
  246. if (settings.ignoreClass) {
  247. all_tds = all_tds.not('.' + settings.ignoreClass);
  248. }
  249. tds = all_tds.filter(':' + settings.filterExpression + '("' + q + '")');
  250. tds.each(function () {
  251. var t = $(this),
  252. col = t.parent().children().index(t);
  253. if ($.inArray(col, settings.ignoreColumns) === -1) {
  254. t.addClass(settings.highlightClass).closest('tr').show().addClass(settings.visibleClass);
  255. }
  256. });
  257. } else {
  258. if (settings.ignoreClass) {
  259. all_tds = all_tds.not('.' + settings.ignoreClass);
  260. }
  261. // highlight (class=alt) only the cells that match the query and show their rows
  262. all_tds.filter(':' + settings.filterExpression + '("' + q + '")').addClass(settings.highlightClass).closest('tr').show().addClass(settings.visibleClass);
  263. }
  264. }
  265. // call the callback function
  266. if (settings.callback) {
  267. settings.callback(q, table);
  268. }
  269. }; // doFiltering()
  270.  
  271. return this.each(function () {
  272. // cache the table
  273. var t = $(this),
  274. // cache the tbody
  275. tbody = t.find('tbody'),
  276. // placeholder for the filter field container DOM node
  277. container = null,
  278. // placeholder for the quick list items
  279. quicks = null,
  280. // placeholder for the field field DOM node
  281. filter = null,
  282. // was the filter created or chosen from an existing element?
  283. created_filter = true;
  284.  
  285. // only if object is a table and there's a tbody and at least minRows trs and hasn't already had a filter added
  286. if (t[0].nodeName === 'TABLE' && tbody.length > 0 && (settings.minRows === 0 || (settings.minRows > 0 && tbody.find('tr').length >= settings.minRows)) && !t.prev().hasClass(settings.containerClass)) {
  287. // use a single existing field as the filter input field
  288. if (settings.inputSelector && $(settings.inputSelector).length === 1) {
  289. filter = $(settings.inputSelector);
  290. // container to hold the quick list options
  291. container = filter.parent();
  292. created_filter = false;
  293. } else {
  294. // create the filter input field (and container)
  295. // build the container tag for the filter field
  296. container = $('<' + settings.containerTag + ' />');
  297. // add any classes that need to be added
  298. if (settings.containerClass !== '') {
  299. container.addClass(settings.containerClass);
  300. }
  301. // add the label for the filter field
  302. container.prepend(settings.label + ' ');
  303. // build the filter field
  304. filter = $('<input type="' + settings.inputType + '" placeholder="' + settings.placeholder + '" name="' + settings.inputName + '" />');
  305. // prevent return in the filter field from submitting any forms
  306. if (settings.preventReturnKey) {
  307. filter.on('keydown', function (ev) {
  308. if ((ev.keyCode || ev.which) === 13) {
  309. ev.preventDefault();
  310. return false;
  311. }
  312. });
  313. }
  314. }
  315.  
  316. // add the autofocus attribute if requested
  317. if (settings.autofocus) {
  318. filter.attr('autofocus', true);
  319. }
  320.  
  321. // does bindWithDelay() exist?
  322. if ($.fn.bindWithDelay) {
  323. // bind doFiltering() to keyup (delayed)
  324. filter.bindWithDelay('keyup', function () {
  325. doFiltering(t, $(this).val());
  326. }, 200);
  327. } else {
  328. // just bind to onKeyUp
  329. // bind doFiltering() to keyup
  330. filter.bind('keyup', function () {
  331. doFiltering(t, $(this).val());
  332. });
  333. }
  334.  
  335. // bind doFiltering() to additional events
  336. filter.bind('click search input paste blur', function () {
  337. doFiltering(t, $(this).val());
  338. });
  339.  
  340. // add the filter field to the container if it was created by the plugin
  341. if (created_filter) {
  342. container.append(filter);
  343. }
  344.  
  345. // are there any quick list items to add?
  346. if (settings.quickList.length > 0 || settings.quickListClear) {
  347. quicks = settings.quickListGroupTag ? $('<' + settings.quickListGroupTag + ' />') : container;
  348. // for each quick list item...
  349. $.each(settings.quickList, function (index, value) {
  350. // build the quick list item link
  351. var q = $('<' + settings.quickListTag + ' class="' + settings.quickListClass + '" />');
  352. // add the item's text
  353. q.text(hsc(value));
  354. if (q[0].nodeName === 'A') {
  355. // add a (worthless) href to the item if it's an anchor tag so that it gets the browser's link treatment
  356. q.attr('href', '#');
  357. }
  358. // bind the click event to it
  359. q.bind('click', function (e) {
  360. // stop the normal anchor tag behavior from happening
  361. e.preventDefault();
  362. // send the quick list value over to the filter field and trigger the event
  363. filter.val(value).focus().trigger('click');
  364. });
  365. // add the quick list link to the quick list groups container
  366. quicks.append(q);
  367. });
  368.  
  369. // add the quick list clear item if a label has been specified
  370. if (settings.quickListClear) {
  371. // build the clear item
  372. var q = $('<' + settings.quickListTag + ' class="' + settings.quickListClass + '" />');
  373. // add the label text
  374. q.html(settings.quickListClear);
  375. if (q[0].nodeName === 'A') {
  376. // add a (worthless) href to the item if it's an anchor tag so that it gets the browser's link treatment
  377. q.attr('href', '#');
  378. }
  379. // bind the click event to it
  380. q.bind('click', function (e) {
  381. e.preventDefault();
  382. // clear the quick list value and trigger the event
  383. filter.val('').focus().trigger('click');
  384. });
  385. // add the clear item to the quick list groups container
  386. quicks.append(q);
  387. }
  388.  
  389. // add the quick list groups container to the DOM if it isn't already there
  390. if (quicks !== container) {
  391. container.append(quicks);
  392. }
  393. }
  394.  
  395. // add the filter field and quick list container to just before the table if it was created by the plugin
  396. if (created_filter) {
  397. t.before(container);
  398. }
  399. }
  400. }); // return this.each
  401. }; // $.fn.filterTable
  402. })(jQuery);