tableresponsive.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**
  2. * @file
  3. * Responsive table functionality.
  4. */
  5. (function ($, Drupal, window) {
  6. 'use strict';
  7. /**
  8. * Attach the tableResponsive function to {@link Drupal.behaviors}.
  9. *
  10. * @type {Drupal~behavior}
  11. *
  12. * @prop {Drupal~behaviorAttach} attach
  13. * Attaches tableResponsive functionality.
  14. */
  15. Drupal.behaviors.tableResponsive = {
  16. attach: function (context, settings) {
  17. var $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
  18. if ($tables.length) {
  19. var il = $tables.length;
  20. for (var i = 0; i < il; i++) {
  21. TableResponsive.tables.push(new TableResponsive($tables[i]));
  22. }
  23. }
  24. }
  25. };
  26. /**
  27. * The TableResponsive object optimizes table presentation for screen size.
  28. *
  29. * A responsive table hides columns at small screen sizes, leaving the most
  30. * important columns visible to the end user. Users should not be prevented
  31. * from accessing all columns, however. This class adds a toggle to a table
  32. * with hidden columns that exposes the columns. Exposing the columns will
  33. * likely break layouts, but it provides the user with a means to access
  34. * data, which is a guiding principle of responsive design.
  35. *
  36. * @constructor Drupal.TableResponsive
  37. *
  38. * @param {HTMLElement} table
  39. * The table element to initialize the responsive table on.
  40. */
  41. function TableResponsive(table) {
  42. this.table = table;
  43. this.$table = $(table);
  44. this.showText = Drupal.t('Show all columns');
  45. this.hideText = Drupal.t('Hide lower priority columns');
  46. // Store a reference to the header elements of the table so that the DOM is
  47. // traversed only once to find them.
  48. this.$headers = this.$table.find('th');
  49. // Add a link before the table for users to show or hide weight columns.
  50. this.$link = $('<button type="button" class="link tableresponsive-toggle"></button>')
  51. .attr('title', Drupal.t('Show table cells that were hidden to make the table fit within a small screen.'))
  52. .on('click', $.proxy(this, 'eventhandlerToggleColumns'));
  53. this.$table.before($('<div class="tableresponsive-toggle-columns"></div>').append(this.$link));
  54. // Attach a resize handler to the window.
  55. $(window)
  56. .on('resize.tableresponsive', $.proxy(this, 'eventhandlerEvaluateColumnVisibility'))
  57. .trigger('resize.tableresponsive');
  58. }
  59. /**
  60. * Extend the TableResponsive function with a list of managed tables.
  61. */
  62. $.extend(TableResponsive, /** @lends Drupal.TableResponsive */{
  63. /**
  64. * Store all created instances.
  65. *
  66. * @type {Array.<Drupal.TableResponsive>}
  67. */
  68. tables: []
  69. });
  70. /**
  71. * Associates an action link with the table that will show hidden columns.
  72. *
  73. * Columns are assumed to be hidden if their header has the class priority-low
  74. * or priority-medium.
  75. */
  76. $.extend(TableResponsive.prototype, /** @lends Drupal.TableResponsive# */{
  77. /**
  78. * @param {jQuery.Event} e
  79. * The event triggered.
  80. */
  81. eventhandlerEvaluateColumnVisibility: function (e) {
  82. var pegged = parseInt(this.$link.data('pegged'), 10);
  83. var hiddenLength = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden').length;
  84. // If the table has hidden columns, associate an action link with the
  85. // table to show the columns.
  86. if (hiddenLength > 0) {
  87. this.$link.show().text(this.showText);
  88. }
  89. // When the toggle is pegged, its presence is maintained because the user
  90. // has interacted with it. This is necessary to keep the link visible if
  91. // the user adjusts screen size and changes the visibility of columns.
  92. if (!pegged && hiddenLength === 0) {
  93. this.$link.hide().text(this.hideText);
  94. }
  95. },
  96. /**
  97. * Toggle the visibility of columns based on their priority.
  98. *
  99. * Columns are classed with either 'priority-low' or 'priority-medium'.
  100. *
  101. * @param {jQuery.Event} e
  102. * The event triggered.
  103. */
  104. eventhandlerToggleColumns: function (e) {
  105. e.preventDefault();
  106. var self = this;
  107. var $hiddenHeaders = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden');
  108. this.$revealedCells = this.$revealedCells || $();
  109. // Reveal hidden columns.
  110. if ($hiddenHeaders.length > 0) {
  111. $hiddenHeaders.each(function (index, element) {
  112. var $header = $(this);
  113. var position = $header.prevAll('th').length;
  114. self.$table.find('tbody tr').each(function () {
  115. var $cells = $(this).find('td').eq(position);
  116. $cells.show();
  117. // Keep track of the revealed cells, so they can be hidden later.
  118. self.$revealedCells = $().add(self.$revealedCells).add($cells);
  119. });
  120. $header.show();
  121. // Keep track of the revealed headers, so they can be hidden later.
  122. self.$revealedCells = $().add(self.$revealedCells).add($header);
  123. });
  124. this.$link.text(this.hideText).data('pegged', 1);
  125. }
  126. // Hide revealed columns.
  127. else {
  128. this.$revealedCells.hide();
  129. // Strip the 'display:none' declaration from the style attributes of
  130. // the table cells that .hide() added.
  131. this.$revealedCells.each(function (index, element) {
  132. var $cell = $(this);
  133. var properties = $cell.attr('style').split(';');
  134. var newProps = [];
  135. // The hide method adds display none to the element. The element
  136. // should be returned to the same state it was in before the columns
  137. // were revealed, so it is necessary to remove the display none value
  138. // from the style attribute.
  139. var match = /^display\s*\:\s*none$/;
  140. for (var i = 0; i < properties.length; i++) {
  141. var prop = properties[i];
  142. prop.trim();
  143. // Find the display:none property and remove it.
  144. var isDisplayNone = match.exec(prop);
  145. if (isDisplayNone) {
  146. continue;
  147. }
  148. newProps.push(prop);
  149. }
  150. // Return the rest of the style attribute values to the element.
  151. $cell.attr('style', newProps.join(';'));
  152. });
  153. this.$link.text(this.showText).data('pegged', 0);
  154. // Refresh the toggle link.
  155. $(window).trigger('resize.tableresponsive');
  156. }
  157. }
  158. });
  159. // Make the TableResponsive object available in the Drupal namespace.
  160. Drupal.TableResponsive = TableResponsive;
  161. })(jQuery, Drupal, window);