Chris@2: /** Chris@2: * jquery.matchHeight.js v0.5.2 Chris@2: * http://brm.io/jquery-match-height/ Chris@2: * License: MIT Chris@2: * https://github.com/liabru/jquery-match-height Chris@2: */ Chris@2: Chris@2: ;(function($) { Chris@2: /* Chris@2: * internal Chris@2: */ Chris@2: Chris@2: var _previousResizeWidth = -1, Chris@2: _updateTimeout = -1; Chris@2: Chris@2: /* Chris@2: * _rows Chris@2: * utility function returns array of jQuery selections representing each row Chris@2: * (as displayed after float wrapping applied by browser) Chris@2: */ Chris@2: Chris@2: var _rows = function(elements) { Chris@2: var tolerance = 1, Chris@2: $elements = $(elements), Chris@2: lastTop = null, Chris@2: rows = []; Chris@2: Chris@2: // group elements by their top position Chris@2: $elements.each(function(){ Chris@2: var $that = $(this), Chris@2: top = $that.offset().top - _parse($that.css('margin-top')), Chris@2: lastRow = rows.length > 0 ? rows[rows.length - 1] : null; Chris@2: Chris@2: if (lastRow === null) { Chris@2: // first item on the row, so just push it Chris@2: rows.push($that); Chris@2: } else { Chris@2: // if the row top is the same, add to the row group Chris@2: if (Math.floor(Math.abs(lastTop - top)) <= tolerance) { Chris@2: rows[rows.length - 1] = lastRow.add($that); Chris@2: } else { Chris@2: // otherwise start a new row group Chris@2: rows.push($that); Chris@2: } Chris@2: } Chris@2: Chris@2: // keep track of the last row top Chris@2: lastTop = top; Chris@2: }); Chris@2: Chris@2: return rows; Chris@2: }; Chris@2: Chris@2: /* Chris@2: * _parse Chris@2: * value parse utility function Chris@2: */ Chris@2: Chris@2: var _parse = function(value) { Chris@2: // parse value and convert NaN to 0 Chris@2: return parseFloat(value) || 0; Chris@2: }; Chris@2: Chris@2: /* Chris@2: * _parseOptions Chris@2: * handle plugin options Chris@2: */ Chris@2: Chris@2: var _parseOptions = function(options) { Chris@2: var opts = { Chris@2: byRow: true, Chris@2: remove: false, Chris@2: property: 'height' Chris@2: }; Chris@2: Chris@2: if (typeof options === 'object') { Chris@2: return $.extend(opts, options); Chris@2: } Chris@2: Chris@2: if (typeof options === 'boolean') { Chris@2: opts.byRow = options; Chris@2: } else if (options === 'remove') { Chris@2: opts.remove = true; Chris@2: } Chris@2: Chris@2: return opts; Chris@2: }; Chris@2: Chris@2: /* Chris@2: * matchHeight Chris@2: * plugin definition Chris@2: */ Chris@2: Chris@2: var matchHeight = $.fn.matchHeight = function(options) { Chris@2: var opts = _parseOptions(options); Chris@2: Chris@2: // handle remove Chris@2: if (opts.remove) { Chris@2: var that = this; Chris@2: Chris@2: // remove fixed height from all selected elements Chris@2: this.css(opts.property, ''); Chris@2: Chris@2: // remove selected elements from all groups Chris@2: $.each(matchHeight._groups, function(key, group) { Chris@2: group.elements = group.elements.not(that); Chris@2: }); Chris@2: Chris@2: // TODO: cleanup empty groups Chris@2: Chris@2: return this; Chris@2: } Chris@2: Chris@2: if (this.length <= 1) Chris@2: return this; Chris@2: Chris@2: // keep track of this group so we can re-apply later on load and resize events Chris@2: matchHeight._groups.push({ Chris@2: elements: this, Chris@2: options: opts Chris@2: }); Chris@2: Chris@2: // match each element's height to the tallest element in the selection Chris@2: matchHeight._apply(this, opts); Chris@2: Chris@2: return this; Chris@2: }; Chris@2: Chris@2: /* Chris@2: * plugin global options Chris@2: */ Chris@2: Chris@2: matchHeight._groups = []; Chris@2: matchHeight._throttle = 80; Chris@2: matchHeight._maintainScroll = false; Chris@2: matchHeight._beforeUpdate = null; Chris@2: matchHeight._afterUpdate = null; Chris@2: Chris@2: /* Chris@2: * matchHeight._apply Chris@2: * apply matchHeight to given elements Chris@2: */ Chris@2: Chris@2: matchHeight._apply = function(elements, options) { Chris@2: var opts = _parseOptions(options), Chris@2: $elements = $(elements), Chris@2: rows = [$elements]; Chris@2: Chris@2: // take note of scroll position Chris@2: var scrollTop = $(window).scrollTop(), Chris@2: htmlHeight = $('html').outerHeight(true); Chris@2: Chris@2: // get hidden parents Chris@2: var $hiddenParents = $elements.parents().filter(':hidden'); Chris@2: Chris@2: // cache the original inline style Chris@2: $hiddenParents.each(function() { Chris@2: var $that = $(this); Chris@2: $that.data('style-cache', $that.attr('style')); Chris@2: }); Chris@2: Chris@2: // temporarily must force hidden parents visible Chris@2: $hiddenParents.css('display', 'block'); Chris@2: Chris@2: // get rows if using byRow, otherwise assume one row Chris@2: if (opts.byRow) { Chris@2: Chris@2: // must first force an arbitrary equal height so floating elements break evenly Chris@2: $elements.each(function() { Chris@2: var $that = $(this), Chris@2: display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block'; Chris@2: Chris@2: // cache the original inline style Chris@2: $that.data('style-cache', $that.attr('style')); Chris@2: Chris@2: $that.css({ Chris@2: 'display': display, Chris@2: 'padding-top': '0', Chris@2: 'padding-bottom': '0', Chris@2: 'margin-top': '0', Chris@2: 'margin-bottom': '0', Chris@2: 'border-top-width': '0', Chris@2: 'border-bottom-width': '0', Chris@2: 'height': '100px' Chris@2: }); Chris@2: }); Chris@2: Chris@2: // get the array of rows (based on element top position) Chris@2: rows = _rows($elements); Chris@2: Chris@2: // revert original inline styles Chris@2: $elements.each(function() { Chris@2: var $that = $(this); Chris@2: $that.attr('style', $that.data('style-cache') || ''); Chris@2: }); Chris@2: } Chris@2: Chris@2: $.each(rows, function(key, row) { Chris@2: var $row = $(row), Chris@2: maxHeight = 0; Chris@2: Chris@2: // skip apply to rows with only one item Chris@2: if (opts.byRow && $row.length <= 1) { Chris@2: $row.css(opts.property, ''); Chris@2: return; Chris@2: } Chris@2: Chris@2: // iterate the row and find the max height Chris@2: $row.each(function(){ Chris@2: var $that = $(this), Chris@2: display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block'; Chris@2: Chris@2: // ensure we get the correct actual height (and not a previously set height value) Chris@2: var css = { 'display': display }; Chris@2: css[opts.property] = ''; Chris@2: $that.css(css); Chris@2: Chris@2: // find the max height (including padding, but not margin) Chris@2: if ($that.outerHeight(false) > maxHeight) Chris@2: maxHeight = $that.outerHeight(false); Chris@2: Chris@2: // revert display block Chris@2: $that.css('display', ''); Chris@2: }); Chris@2: Chris@2: // iterate the row and apply the height to all elements Chris@2: $row.each(function(){ Chris@2: var $that = $(this), Chris@2: verticalPadding = 0; Chris@2: Chris@2: // handle padding and border correctly (required when not using border-box) Chris@2: if ($that.css('box-sizing') !== 'border-box') { Chris@2: verticalPadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width')); Chris@2: verticalPadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom')); Chris@2: } Chris@2: Chris@2: // set the height (accounting for padding and border) Chris@2: $that.css(opts.property, maxHeight - verticalPadding); Chris@2: }); Chris@2: }); Chris@2: Chris@2: // revert hidden parents Chris@2: $hiddenParents.each(function() { Chris@2: var $that = $(this); Chris@2: $that.attr('style', $that.data('style-cache') || null); Chris@2: }); Chris@2: Chris@2: // restore scroll position if enabled Chris@2: if (matchHeight._maintainScroll) Chris@2: $(window).scrollTop((scrollTop / htmlHeight) * $('html').outerHeight(true)); Chris@2: Chris@2: return this; Chris@2: }; Chris@2: Chris@2: /* Chris@2: * matchHeight._applyDataApi Chris@2: * applies matchHeight to all elements with a data-match-height attribute Chris@2: */ Chris@2: Chris@2: matchHeight._applyDataApi = function() { Chris@2: var groups = {}; Chris@2: Chris@2: // generate groups by their groupId set by elements using data-match-height Chris@2: $('[data-match-height], [data-mh]').each(function() { Chris@2: var $this = $(this), Chris@2: groupId = $this.attr('data-match-height') || $this.attr('data-mh'); Chris@2: if (groupId in groups) { Chris@2: groups[groupId] = groups[groupId].add($this); Chris@2: } else { Chris@2: groups[groupId] = $this; Chris@2: } Chris@2: }); Chris@2: Chris@2: // apply matchHeight to each group Chris@2: $.each(groups, function() { Chris@2: this.matchHeight(true); Chris@2: }); Chris@2: }; Chris@2: Chris@2: /* Chris@2: * matchHeight._update Chris@2: * updates matchHeight on all current groups with their correct options Chris@2: */ Chris@2: Chris@2: var _update = function(event) { Chris@2: if (matchHeight._beforeUpdate) Chris@2: matchHeight._beforeUpdate(event, matchHeight._groups); Chris@2: Chris@2: $.each(matchHeight._groups, function() { Chris@2: matchHeight._apply(this.elements, this.options); Chris@2: }); Chris@2: Chris@2: if (matchHeight._afterUpdate) Chris@2: matchHeight._afterUpdate(event, matchHeight._groups); Chris@2: }; Chris@2: Chris@2: matchHeight._update = function(throttle, event) { Chris@2: // prevent update if fired from a resize event Chris@2: // where the viewport width hasn't actually changed Chris@2: // fixes an event looping bug in IE8 Chris@2: if (event && event.type === 'resize') { Chris@2: var windowWidth = $(window).width(); Chris@2: if (windowWidth === _previousResizeWidth) Chris@2: return; Chris@2: _previousResizeWidth = windowWidth; Chris@2: } Chris@2: Chris@2: // throttle updates Chris@2: if (!throttle) { Chris@2: _update(event); Chris@2: } else if (_updateTimeout === -1) { Chris@2: _updateTimeout = setTimeout(function() { Chris@2: _update(event); Chris@2: _updateTimeout = -1; Chris@2: }, matchHeight._throttle); Chris@2: } Chris@2: }; Chris@2: Chris@2: /* Chris@2: * bind events Chris@2: */ Chris@2: Chris@2: // apply on DOM ready event Chris@2: $(matchHeight._applyDataApi); Chris@2: Chris@2: // update heights on load and resize events Chris@2: $(window).bind('load', function(event) { Chris@2: matchHeight._update(false, event); Chris@2: }); Chris@2: Chris@2: // throttled update heights on resize events Chris@2: $(window).bind('resize orientationchange', function(event) { Chris@2: matchHeight._update(true, event); Chris@2: }); Chris@2: Chris@2: })(jQuery); Chris@2: Chris@2: Chris@2: // Added on to trigger the script above and give equal heights to some Mayo columns. Chris@2: (function ($) { Chris@2: $(document).ready(function() { Chris@2: $('#top-columns .column-block').matchHeight(); Chris@2: $('#bottom-columns .column-block').matchHeight(); Chris@2: }); Chris@2: })(jQuery);