jquery.matchheight.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /**
  2. * jquery.matchHeight.js v0.5.2
  3. * http://brm.io/jquery-match-height/
  4. * License: MIT
  5. */
  6. (function($) {
  7. $.fn.matchHeight = function(byRow) {
  8. // handle matchHeight('remove')
  9. if (byRow === 'remove') {
  10. var that = this;
  11. // remove fixed height from all selected elements
  12. this.css('height', '');
  13. // remove selected elements from all groups
  14. $.each($.fn.matchHeight._groups, function(key, group) {
  15. group.elements = group.elements.not(that);
  16. });
  17. // TODO: cleanup empty groups
  18. return this;
  19. }
  20. if (this.length <= 1)
  21. return this;
  22. // byRow default to true
  23. byRow = (typeof byRow !== 'undefined') ? byRow : true;
  24. // keep track of this group so we can re-apply later on load and resize events
  25. $.fn.matchHeight._groups.push({
  26. elements: this,
  27. byRow: byRow
  28. });
  29. // match each element's height to the tallest element in the selection
  30. $.fn.matchHeight._apply(this, byRow);
  31. return this;
  32. };
  33. $.fn.matchHeight._apply = function(elements, byRow) {
  34. var $elements = $(elements),
  35. rows = [$elements];
  36. // get rows if using byRow, otherwise assume one row
  37. if (byRow) {
  38. // must first force an arbitrary equal height so floating elements break evenly
  39. $elements.css({
  40. 'display': 'block',
  41. 'padding-top': '0',
  42. 'padding-bottom': '0',
  43. 'border-top-width': '0',
  44. 'border-bottom-width': '0',
  45. 'height': '100px'
  46. });
  47. // get the array of rows (based on element top position)
  48. rows = _rows($elements);
  49. // revert the temporary forced style
  50. $elements.css({
  51. 'display': '',
  52. 'padding-top': '',
  53. 'padding-bottom': '',
  54. 'border-top-width': '',
  55. 'border-bottom-width': '',
  56. 'height': ''
  57. });
  58. }
  59. $.each(rows, function(key, row) {
  60. var $row = $(row),
  61. maxHeight = 0;
  62. // ensure elements are visible to prevent 0 height
  63. var hiddenParents = $row.parents().add($row).filter(':hidden');
  64. hiddenParents.css({ 'display': 'block' });
  65. // iterate the row and find the max height
  66. $row.each(function(){
  67. var $that = $(this);
  68. // ensure we get the correct actual height (and not a previously set height value)
  69. $that.css({ 'display': 'block', 'height': '' });
  70. // find the max height (including padding, but not margin)
  71. if ($that.outerHeight(false) > maxHeight)
  72. maxHeight = $that.outerHeight(false);
  73. // revert display block
  74. $that.css({ 'display': '' });
  75. });
  76. // revert display block
  77. hiddenParents.css({ 'display': '' });
  78. // iterate the row and apply the height to all elements
  79. $row.each(function(){
  80. var $that = $(this),
  81. verticalPadding = 0;
  82. // handle padding and border correctly (required when not using border-box)
  83. if ($that.css('box-sizing') !== 'border-box') {
  84. verticalPadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width'));
  85. verticalPadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom'));
  86. }
  87. // set the height (accounting for padding and border)
  88. $that.css('height', maxHeight - verticalPadding);
  89. });
  90. });
  91. return this;
  92. };
  93. /*
  94. * _applyDataApi will apply matchHeight to all elements with a data-match-height attribute
  95. */
  96. $.fn.matchHeight._applyDataApi = function() {
  97. var groups = {};
  98. // generate groups by their groupId set by elements using data-match-height
  99. $('[data-match-height], [data-mh]').each(function() {
  100. var $this = $(this),
  101. groupId = $this.attr('data-match-height');
  102. if (groupId in groups) {
  103. groups[groupId] = groups[groupId].add($this);
  104. } else {
  105. groups[groupId] = $this;
  106. }
  107. });
  108. // apply matchHeight to each group
  109. $.each(groups, function() {
  110. this.matchHeight(true);
  111. });
  112. };
  113. /*
  114. * _update function will re-apply matchHeight to all groups with the correct options
  115. */
  116. $.fn.matchHeight._groups = [];
  117. $.fn.matchHeight._throttle = 80;
  118. var previousResizeWidth = -1,
  119. updateTimeout = -1;
  120. $.fn.matchHeight._update = function(event) {
  121. // prevent update if fired from a resize event
  122. // where the viewport width hasn't actually changed
  123. // fixes an event looping bug in IE8
  124. if (event && event.type === 'resize') {
  125. var windowWidth = $(window).width();
  126. if (windowWidth === previousResizeWidth)
  127. return;
  128. previousResizeWidth = windowWidth;
  129. }
  130. // throttle updates
  131. if (updateTimeout === -1) {
  132. updateTimeout = setTimeout(function() {
  133. $.each($.fn.matchHeight._groups, function() {
  134. $.fn.matchHeight._apply(this.elements, this.byRow);
  135. });
  136. updateTimeout = -1;
  137. }, $.fn.matchHeight._throttle);
  138. }
  139. };
  140. /*
  141. * bind events
  142. */
  143. // apply on DOM ready event
  144. $($.fn.matchHeight._applyDataApi);
  145. // update heights on load and resize events
  146. $(window).bind('load resize orientationchange', $.fn.matchHeight._update);
  147. /*
  148. * rows utility function
  149. * returns array of jQuery selections representing each row
  150. * (as displayed after float wrapping applied by browser)
  151. */
  152. var _rows = function(elements) {
  153. var tolerance = 1,
  154. $elements = $(elements),
  155. lastTop = null,
  156. rows = [];
  157. // group elements by their top position
  158. $elements.each(function(){
  159. var $that = $(this),
  160. top = $that.offset().top - _parse($that.css('margin-top')),
  161. lastRow = rows.length > 0 ? rows[rows.length - 1] : null;
  162. if (lastRow === null) {
  163. // first item on the row, so just push it
  164. rows.push($that);
  165. } else {
  166. // if the row top is the same, add to the row group
  167. if (Math.floor(Math.abs(lastTop - top)) <= tolerance) {
  168. rows[rows.length - 1] = lastRow.add($that);
  169. } else {
  170. // otherwise start a new row group
  171. rows.push($that);
  172. }
  173. }
  174. // keep track of the last row top
  175. lastTop = top;
  176. });
  177. return rows;
  178. };
  179. var _parse = function(value) {
  180. // parse value and convert NaN to 0
  181. return parseFloat(value) || 0;
  182. };
  183. })(jQuery);