Chris@2
|
1 /**
|
Chris@2
|
2 * jquery.matchHeight.js v0.5.2
|
Chris@2
|
3 * http://brm.io/jquery-match-height/
|
Chris@2
|
4 * License: MIT
|
Chris@2
|
5 * https://github.com/liabru/jquery-match-height
|
Chris@2
|
6 */
|
Chris@2
|
7
|
Chris@2
|
8 ;(function($) {
|
Chris@2
|
9 /*
|
Chris@2
|
10 * internal
|
Chris@2
|
11 */
|
Chris@2
|
12
|
Chris@2
|
13 var _previousResizeWidth = -1,
|
Chris@2
|
14 _updateTimeout = -1;
|
Chris@2
|
15
|
Chris@2
|
16 /*
|
Chris@2
|
17 * _rows
|
Chris@2
|
18 * utility function returns array of jQuery selections representing each row
|
Chris@2
|
19 * (as displayed after float wrapping applied by browser)
|
Chris@2
|
20 */
|
Chris@2
|
21
|
Chris@2
|
22 var _rows = function(elements) {
|
Chris@2
|
23 var tolerance = 1,
|
Chris@2
|
24 $elements = $(elements),
|
Chris@2
|
25 lastTop = null,
|
Chris@2
|
26 rows = [];
|
Chris@2
|
27
|
Chris@2
|
28 // group elements by their top position
|
Chris@2
|
29 $elements.each(function(){
|
Chris@2
|
30 var $that = $(this),
|
Chris@2
|
31 top = $that.offset().top - _parse($that.css('margin-top')),
|
Chris@2
|
32 lastRow = rows.length > 0 ? rows[rows.length - 1] : null;
|
Chris@2
|
33
|
Chris@2
|
34 if (lastRow === null) {
|
Chris@2
|
35 // first item on the row, so just push it
|
Chris@2
|
36 rows.push($that);
|
Chris@2
|
37 } else {
|
Chris@2
|
38 // if the row top is the same, add to the row group
|
Chris@2
|
39 if (Math.floor(Math.abs(lastTop - top)) <= tolerance) {
|
Chris@2
|
40 rows[rows.length - 1] = lastRow.add($that);
|
Chris@2
|
41 } else {
|
Chris@2
|
42 // otherwise start a new row group
|
Chris@2
|
43 rows.push($that);
|
Chris@2
|
44 }
|
Chris@2
|
45 }
|
Chris@2
|
46
|
Chris@2
|
47 // keep track of the last row top
|
Chris@2
|
48 lastTop = top;
|
Chris@2
|
49 });
|
Chris@2
|
50
|
Chris@2
|
51 return rows;
|
Chris@2
|
52 };
|
Chris@2
|
53
|
Chris@2
|
54 /*
|
Chris@2
|
55 * _parse
|
Chris@2
|
56 * value parse utility function
|
Chris@2
|
57 */
|
Chris@2
|
58
|
Chris@2
|
59 var _parse = function(value) {
|
Chris@2
|
60 // parse value and convert NaN to 0
|
Chris@2
|
61 return parseFloat(value) || 0;
|
Chris@2
|
62 };
|
Chris@2
|
63
|
Chris@2
|
64 /*
|
Chris@2
|
65 * _parseOptions
|
Chris@2
|
66 * handle plugin options
|
Chris@2
|
67 */
|
Chris@2
|
68
|
Chris@2
|
69 var _parseOptions = function(options) {
|
Chris@2
|
70 var opts = {
|
Chris@2
|
71 byRow: true,
|
Chris@2
|
72 remove: false,
|
Chris@2
|
73 property: 'height'
|
Chris@2
|
74 };
|
Chris@2
|
75
|
Chris@2
|
76 if (typeof options === 'object') {
|
Chris@2
|
77 return $.extend(opts, options);
|
Chris@2
|
78 }
|
Chris@2
|
79
|
Chris@2
|
80 if (typeof options === 'boolean') {
|
Chris@2
|
81 opts.byRow = options;
|
Chris@2
|
82 } else if (options === 'remove') {
|
Chris@2
|
83 opts.remove = true;
|
Chris@2
|
84 }
|
Chris@2
|
85
|
Chris@2
|
86 return opts;
|
Chris@2
|
87 };
|
Chris@2
|
88
|
Chris@2
|
89 /*
|
Chris@2
|
90 * matchHeight
|
Chris@2
|
91 * plugin definition
|
Chris@2
|
92 */
|
Chris@2
|
93
|
Chris@2
|
94 var matchHeight = $.fn.matchHeight = function(options) {
|
Chris@2
|
95 var opts = _parseOptions(options);
|
Chris@2
|
96
|
Chris@2
|
97 // handle remove
|
Chris@2
|
98 if (opts.remove) {
|
Chris@2
|
99 var that = this;
|
Chris@2
|
100
|
Chris@2
|
101 // remove fixed height from all selected elements
|
Chris@2
|
102 this.css(opts.property, '');
|
Chris@2
|
103
|
Chris@2
|
104 // remove selected elements from all groups
|
Chris@2
|
105 $.each(matchHeight._groups, function(key, group) {
|
Chris@2
|
106 group.elements = group.elements.not(that);
|
Chris@2
|
107 });
|
Chris@2
|
108
|
Chris@2
|
109 // TODO: cleanup empty groups
|
Chris@2
|
110
|
Chris@2
|
111 return this;
|
Chris@2
|
112 }
|
Chris@2
|
113
|
Chris@2
|
114 if (this.length <= 1)
|
Chris@2
|
115 return this;
|
Chris@2
|
116
|
Chris@2
|
117 // keep track of this group so we can re-apply later on load and resize events
|
Chris@2
|
118 matchHeight._groups.push({
|
Chris@2
|
119 elements: this,
|
Chris@2
|
120 options: opts
|
Chris@2
|
121 });
|
Chris@2
|
122
|
Chris@2
|
123 // match each element's height to the tallest element in the selection
|
Chris@2
|
124 matchHeight._apply(this, opts);
|
Chris@2
|
125
|
Chris@2
|
126 return this;
|
Chris@2
|
127 };
|
Chris@2
|
128
|
Chris@2
|
129 /*
|
Chris@2
|
130 * plugin global options
|
Chris@2
|
131 */
|
Chris@2
|
132
|
Chris@2
|
133 matchHeight._groups = [];
|
Chris@2
|
134 matchHeight._throttle = 80;
|
Chris@2
|
135 matchHeight._maintainScroll = false;
|
Chris@2
|
136 matchHeight._beforeUpdate = null;
|
Chris@2
|
137 matchHeight._afterUpdate = null;
|
Chris@2
|
138
|
Chris@2
|
139 /*
|
Chris@2
|
140 * matchHeight._apply
|
Chris@2
|
141 * apply matchHeight to given elements
|
Chris@2
|
142 */
|
Chris@2
|
143
|
Chris@2
|
144 matchHeight._apply = function(elements, options) {
|
Chris@2
|
145 var opts = _parseOptions(options),
|
Chris@2
|
146 $elements = $(elements),
|
Chris@2
|
147 rows = [$elements];
|
Chris@2
|
148
|
Chris@2
|
149 // take note of scroll position
|
Chris@2
|
150 var scrollTop = $(window).scrollTop(),
|
Chris@2
|
151 htmlHeight = $('html').outerHeight(true);
|
Chris@2
|
152
|
Chris@2
|
153 // get hidden parents
|
Chris@2
|
154 var $hiddenParents = $elements.parents().filter(':hidden');
|
Chris@2
|
155
|
Chris@2
|
156 // cache the original inline style
|
Chris@2
|
157 $hiddenParents.each(function() {
|
Chris@2
|
158 var $that = $(this);
|
Chris@2
|
159 $that.data('style-cache', $that.attr('style'));
|
Chris@2
|
160 });
|
Chris@2
|
161
|
Chris@2
|
162 // temporarily must force hidden parents visible
|
Chris@2
|
163 $hiddenParents.css('display', 'block');
|
Chris@2
|
164
|
Chris@2
|
165 // get rows if using byRow, otherwise assume one row
|
Chris@2
|
166 if (opts.byRow) {
|
Chris@2
|
167
|
Chris@2
|
168 // must first force an arbitrary equal height so floating elements break evenly
|
Chris@2
|
169 $elements.each(function() {
|
Chris@2
|
170 var $that = $(this),
|
Chris@2
|
171 display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block';
|
Chris@2
|
172
|
Chris@2
|
173 // cache the original inline style
|
Chris@2
|
174 $that.data('style-cache', $that.attr('style'));
|
Chris@2
|
175
|
Chris@2
|
176 $that.css({
|
Chris@2
|
177 'display': display,
|
Chris@2
|
178 'padding-top': '0',
|
Chris@2
|
179 'padding-bottom': '0',
|
Chris@2
|
180 'margin-top': '0',
|
Chris@2
|
181 'margin-bottom': '0',
|
Chris@2
|
182 'border-top-width': '0',
|
Chris@2
|
183 'border-bottom-width': '0',
|
Chris@2
|
184 'height': '100px'
|
Chris@2
|
185 });
|
Chris@2
|
186 });
|
Chris@2
|
187
|
Chris@2
|
188 // get the array of rows (based on element top position)
|
Chris@2
|
189 rows = _rows($elements);
|
Chris@2
|
190
|
Chris@2
|
191 // revert original inline styles
|
Chris@2
|
192 $elements.each(function() {
|
Chris@2
|
193 var $that = $(this);
|
Chris@2
|
194 $that.attr('style', $that.data('style-cache') || '');
|
Chris@2
|
195 });
|
Chris@2
|
196 }
|
Chris@2
|
197
|
Chris@2
|
198 $.each(rows, function(key, row) {
|
Chris@2
|
199 var $row = $(row),
|
Chris@2
|
200 maxHeight = 0;
|
Chris@2
|
201
|
Chris@2
|
202 // skip apply to rows with only one item
|
Chris@2
|
203 if (opts.byRow && $row.length <= 1) {
|
Chris@2
|
204 $row.css(opts.property, '');
|
Chris@2
|
205 return;
|
Chris@2
|
206 }
|
Chris@2
|
207
|
Chris@2
|
208 // iterate the row and find the max height
|
Chris@2
|
209 $row.each(function(){
|
Chris@2
|
210 var $that = $(this),
|
Chris@2
|
211 display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block';
|
Chris@2
|
212
|
Chris@2
|
213 // ensure we get the correct actual height (and not a previously set height value)
|
Chris@2
|
214 var css = { 'display': display };
|
Chris@2
|
215 css[opts.property] = '';
|
Chris@2
|
216 $that.css(css);
|
Chris@2
|
217
|
Chris@2
|
218 // find the max height (including padding, but not margin)
|
Chris@2
|
219 if ($that.outerHeight(false) > maxHeight)
|
Chris@2
|
220 maxHeight = $that.outerHeight(false);
|
Chris@2
|
221
|
Chris@2
|
222 // revert display block
|
Chris@2
|
223 $that.css('display', '');
|
Chris@2
|
224 });
|
Chris@2
|
225
|
Chris@2
|
226 // iterate the row and apply the height to all elements
|
Chris@2
|
227 $row.each(function(){
|
Chris@2
|
228 var $that = $(this),
|
Chris@2
|
229 verticalPadding = 0;
|
Chris@2
|
230
|
Chris@2
|
231 // handle padding and border correctly (required when not using border-box)
|
Chris@2
|
232 if ($that.css('box-sizing') !== 'border-box') {
|
Chris@2
|
233 verticalPadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width'));
|
Chris@2
|
234 verticalPadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom'));
|
Chris@2
|
235 }
|
Chris@2
|
236
|
Chris@2
|
237 // set the height (accounting for padding and border)
|
Chris@2
|
238 $that.css(opts.property, maxHeight - verticalPadding);
|
Chris@2
|
239 });
|
Chris@2
|
240 });
|
Chris@2
|
241
|
Chris@2
|
242 // revert hidden parents
|
Chris@2
|
243 $hiddenParents.each(function() {
|
Chris@2
|
244 var $that = $(this);
|
Chris@2
|
245 $that.attr('style', $that.data('style-cache') || null);
|
Chris@2
|
246 });
|
Chris@2
|
247
|
Chris@2
|
248 // restore scroll position if enabled
|
Chris@2
|
249 if (matchHeight._maintainScroll)
|
Chris@2
|
250 $(window).scrollTop((scrollTop / htmlHeight) * $('html').outerHeight(true));
|
Chris@2
|
251
|
Chris@2
|
252 return this;
|
Chris@2
|
253 };
|
Chris@2
|
254
|
Chris@2
|
255 /*
|
Chris@2
|
256 * matchHeight._applyDataApi
|
Chris@2
|
257 * applies matchHeight to all elements with a data-match-height attribute
|
Chris@2
|
258 */
|
Chris@2
|
259
|
Chris@2
|
260 matchHeight._applyDataApi = function() {
|
Chris@2
|
261 var groups = {};
|
Chris@2
|
262
|
Chris@2
|
263 // generate groups by their groupId set by elements using data-match-height
|
Chris@2
|
264 $('[data-match-height], [data-mh]').each(function() {
|
Chris@2
|
265 var $this = $(this),
|
Chris@2
|
266 groupId = $this.attr('data-match-height') || $this.attr('data-mh');
|
Chris@2
|
267 if (groupId in groups) {
|
Chris@2
|
268 groups[groupId] = groups[groupId].add($this);
|
Chris@2
|
269 } else {
|
Chris@2
|
270 groups[groupId] = $this;
|
Chris@2
|
271 }
|
Chris@2
|
272 });
|
Chris@2
|
273
|
Chris@2
|
274 // apply matchHeight to each group
|
Chris@2
|
275 $.each(groups, function() {
|
Chris@2
|
276 this.matchHeight(true);
|
Chris@2
|
277 });
|
Chris@2
|
278 };
|
Chris@2
|
279
|
Chris@2
|
280 /*
|
Chris@2
|
281 * matchHeight._update
|
Chris@2
|
282 * updates matchHeight on all current groups with their correct options
|
Chris@2
|
283 */
|
Chris@2
|
284
|
Chris@2
|
285 var _update = function(event) {
|
Chris@2
|
286 if (matchHeight._beforeUpdate)
|
Chris@2
|
287 matchHeight._beforeUpdate(event, matchHeight._groups);
|
Chris@2
|
288
|
Chris@2
|
289 $.each(matchHeight._groups, function() {
|
Chris@2
|
290 matchHeight._apply(this.elements, this.options);
|
Chris@2
|
291 });
|
Chris@2
|
292
|
Chris@2
|
293 if (matchHeight._afterUpdate)
|
Chris@2
|
294 matchHeight._afterUpdate(event, matchHeight._groups);
|
Chris@2
|
295 };
|
Chris@2
|
296
|
Chris@2
|
297 matchHeight._update = function(throttle, event) {
|
Chris@2
|
298 // prevent update if fired from a resize event
|
Chris@2
|
299 // where the viewport width hasn't actually changed
|
Chris@2
|
300 // fixes an event looping bug in IE8
|
Chris@2
|
301 if (event && event.type === 'resize') {
|
Chris@2
|
302 var windowWidth = $(window).width();
|
Chris@2
|
303 if (windowWidth === _previousResizeWidth)
|
Chris@2
|
304 return;
|
Chris@2
|
305 _previousResizeWidth = windowWidth;
|
Chris@2
|
306 }
|
Chris@2
|
307
|
Chris@2
|
308 // throttle updates
|
Chris@2
|
309 if (!throttle) {
|
Chris@2
|
310 _update(event);
|
Chris@2
|
311 } else if (_updateTimeout === -1) {
|
Chris@2
|
312 _updateTimeout = setTimeout(function() {
|
Chris@2
|
313 _update(event);
|
Chris@2
|
314 _updateTimeout = -1;
|
Chris@2
|
315 }, matchHeight._throttle);
|
Chris@2
|
316 }
|
Chris@2
|
317 };
|
Chris@2
|
318
|
Chris@2
|
319 /*
|
Chris@2
|
320 * bind events
|
Chris@2
|
321 */
|
Chris@2
|
322
|
Chris@2
|
323 // apply on DOM ready event
|
Chris@2
|
324 $(matchHeight._applyDataApi);
|
Chris@2
|
325
|
Chris@2
|
326 // update heights on load and resize events
|
Chris@2
|
327 $(window).bind('load', function(event) {
|
Chris@2
|
328 matchHeight._update(false, event);
|
Chris@2
|
329 });
|
Chris@2
|
330
|
Chris@2
|
331 // throttled update heights on resize events
|
Chris@2
|
332 $(window).bind('resize orientationchange', function(event) {
|
Chris@2
|
333 matchHeight._update(true, event);
|
Chris@2
|
334 });
|
Chris@2
|
335
|
Chris@2
|
336 })(jQuery);
|
Chris@2
|
337
|
Chris@2
|
338
|
Chris@2
|
339 // Added on to trigger the script above and give equal heights to some Mayo columns.
|
Chris@2
|
340 (function ($) {
|
Chris@2
|
341 $(document).ready(function() {
|
Chris@2
|
342 $('#top-columns .column-block').matchHeight();
|
Chris@2
|
343 $('#bottom-columns .column-block').matchHeight();
|
Chris@2
|
344 });
|
Chris@2
|
345 })(jQuery); |