/** * jquery-match-height 0.7.0 by @liabru * http://brm.io/jquery-match-height/ * license: mit */ ;(function(factory) { // eslint-disable-line no-extra-semi 'use strict'; if (typeof define === 'function' && define.amd) { // amd define(['jquery'], factory); } else if (typeof module !== 'undefined' && module.exports) { // commonjs module.exports = factory(require('jquery')); } else { // global factory(jquery); } })(function($) { /* * internal */ var _previousresizewidth = -1, _updatetimeout = -1; /* * _parse * value parse utility function */ var _parse = function(value) { // parse value and convert nan to 0 return parsefloat(value) || 0; }; /* * _rows * utility function returns array of jquery selections representing each row * (as displayed after float wrapping applied by browser) */ var _rows = function(elements) { var tolerance = 1, $elements = $(elements), lasttop = null, rows = []; // group elements by their top position $elements.each(function(){ var $that = $(this), top = $that.offset().top - _parse($that.css('margin-top')), lastrow = rows.length > 0 ? rows[rows.length - 1] : null; if (lastrow === null) { // first item on the row, so just push it rows.push($that); } else { // if the row top is the same, add to the row group if (math.floor(math.abs(lasttop - top)) <= tolerance) { rows[rows.length - 1] = lastrow.add($that); } else { // otherwise start a new row group rows.push($that); } } // keep track of the last row top lasttop = top; }); return rows; }; /* * _parseoptions * handle plugin options */ var _parseoptions = function(options) { var opts = { byrow: true, property: 'height', target: null, remove: false }; if (typeof options === 'object') { return $.extend(opts, options); } if (typeof options === 'boolean') { opts.byrow = options; } else if (options === 'remove') { opts.remove = true; } return opts; }; /* * matchheight * plugin definition */ var matchheight = $.fn.matchheight = function(options) { var opts = _parseoptions(options); // handle remove if (opts.remove) { var that = this; // remove fixed height from all selected elements this.css(opts.property, ''); // remove selected elements from all groups $.each(matchheight._groups, function(key, group) { group.elements = group.elements.not(that); }); // todo: cleanup empty groups return this; } if (this.length <= 1 && !opts.target) { return this; } // keep track of this group so we can re-apply later on load and resize events matchheight._groups.push({ elements: this, options: opts }); // match each element's height to the tallest element in the selection matchheight._apply(this, opts); return this; }; /* * plugin global options */ matchheight.version = '0.7.0'; matchheight._groups = []; matchheight._throttle = 80; matchheight._maintainscroll = false; matchheight._beforeupdate = null; matchheight._afterupdate = null; matchheight._rows = _rows; matchheight._parse = _parse; matchheight._parseoptions = _parseoptions; /* * matchheight._apply * apply matchheight to given elements */ matchheight._apply = function(elements, options) { var opts = _parseoptions(options), $elements = $(elements), rows = [$elements]; // take note of scroll position var scrolltop = $(window).scrolltop(), htmlheight = $('html').outerheight(true); // get hidden parents var $hiddenparents = $elements.parents().filter(':hidden'); // cache the original inline style $hiddenparents.each(function() { var $that = $(this); $that.data('style-cache', $that.attr('style')); }); // temporarily must force hidden parents visible $hiddenparents.css('display', 'block'); // get rows if using byrow, otherwise assume one row if (opts.byrow && !opts.target) { // must first force an arbitrary equal height so floating elements break evenly $elements.each(function() { var $that = $(this), display = $that.css('display'); // temporarily force a usable display value if (display !== 'inline-block' && display !== 'flex' && display !== 'inline-flex') { display = 'block'; } // cache the original inline style $that.data('style-cache', $that.attr('style')); $that.css({ 'display': display, 'padding-top': '0', 'padding-bottom': '0', 'margin-top': '0', 'margin-bottom': '0', 'border-top-width': '0', 'border-bottom-width': '0', 'height': '100px', 'overflow': 'hidden' }); }); // get the array of rows (based on element top position) rows = _rows($elements); // revert original inline styles $elements.each(function() { var $that = $(this); $that.attr('style', $that.data('style-cache') || ''); }); } $.each(rows, function(key, row) { var $row = $(row), targetheight = 0; if (!opts.target) { // skip apply to rows with only one item if (opts.byrow && $row.length <= 1) { $row.css(opts.property, ''); return; } // iterate the row and find the max height $row.each(function(){ var $that = $(this), style = $that.attr('style'), display = $that.css('display'); // temporarily force a usable display value if (display !== 'inline-block' && display !== 'flex' && display !== 'inline-flex') { display = 'block'; } // ensure we get the correct actual height (and not a previously set height value) var css = { 'display': display }; css[opts.property] = ''; $that.css(css); // find the max height (including padding, but not margin) if ($that.outerheight(false) > targetheight) { targetheight = $that.outerheight(false); } // revert styles if (style) { $that.attr('style', style); } else { $that.css('display', ''); } }); } else { // if target set, use the height of the target element targetheight = opts.target.outerheight(false); } // iterate the row and apply the height to all elements $row.each(function(){ var $that = $(this), verticalpadding = 0; // don't apply to a target if (opts.target && $that.is(opts.target)) { return; } // handle padding and border correctly (required when not using border-box) if ($that.css('box-sizing') !== 'border-box') { verticalpadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width')); verticalpadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom')); } // set the height (accounting for padding and border) $that.css(opts.property, (targetheight - verticalpadding) + 'px'); }); }); // revert hidden parents $hiddenparents.each(function() { var $that = $(this); $that.attr('style', $that.data('style-cache') || null); }); // restore scroll position if enabled if (matchheight._maintainscroll) { $(window).scrolltop((scrolltop / htmlheight) * $('html').outerheight(true)); } return this; }; /* * matchheight._applydataapi * applies matchheight to all elements with a data-match-height attribute */ matchheight._applydataapi = function() { var groups = {}; // generate groups by their groupid set by elements using data-match-height $('[data-match-height], [data-mh]').each(function() { var $this = $(this), groupid = $this.attr('data-mh') || $this.attr('data-match-height'); if (groupid in groups) { groups[groupid] = groups[groupid].add($this); } else { groups[groupid] = $this; } }); // apply matchheight to each group $.each(groups, function() { this.matchheight(true); }); }; /* * matchheight._update * updates matchheight on all current groups with their correct options */ var _update = function(event) { if (matchheight._beforeupdate) { matchheight._beforeupdate(event, matchheight._groups); } $.each(matchheight._groups, function() { matchheight._apply(this.elements, this.options); }); if (matchheight._afterupdate) { matchheight._afterupdate(event, matchheight._groups); } }; matchheight._update = function(throttle, event) { // prevent update if fired from a resize event // where the viewport width hasn't actually changed // fixes an event looping bug in ie8 if (event && event.type === 'resize') { var windowwidth = $(window).width(); if (windowwidth === _previousresizewidth) { return; } _previousresizewidth = windowwidth; } // throttle updates if (!throttle) { _update(event); } else if (_updatetimeout === -1) { _updatetimeout = settimeout(function() { _update(event); _updatetimeout = -1; }, matchheight._throttle); } }; /* * bind events */ // apply on dom ready event $(matchheight._applydataapi); // update heights on load and resize events $(window).bind('load', function(event) { matchheight._update(false, event); }); // throttled update heights on resize events $(window).bind('resize orientationchange', function(event) { matchheight._update(true, event); }); });