[interchange] Switch jQuery "equal heights" plugins
Josh Lavin <[email protected]> Mon, 22 Aug 2016 23:35:41 +0000
| Newsgroups | gmane.comp.web.interchange.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit a9f9a979cad896ac0e80fc1af3051423f56434df Author: Josh Lavin <[email protected]> Date: Mon Aug 22 16:29:36 2016 -0700 Switch jQuery "equal heights" plugins - former plugin wasn't working in real-life use cases, for results pages with multiple categories -- it would leave an orphan right-aligned as the last item. - the "matchHeights" plugin can group all blocks, so in results.html, we are grouping by the block in each category. - make results page wider by omitting right-hand component and column. Adjust block widths afterwards. .../strap/html/js/jquery.equalheightcolumns.min.js | 2 - dist/strap/html/js/jquery.matchHeight.js | 385 ++++++++++++++++++++ dist/strap/pages/results.html | 12 +- dist/strap/templates/components/cross | 4 +- dist/strap/variables/JS | 8 +- 5 files changed, 392 insertions(+), 19 deletions(-) --- diff --git a/dist/strap/html/js/jquery.matchHeight.js b/dist/strap/html/js/jquery.matchHeight.js new file mode 100644 index 0000000..ab24d45 --- /dev/null +++ b/dist/strap/html/js/jquery.matchHeight.js @@ -0,0 +1,385 @@ +/** +* jquery-match-height master 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 = 'master'; + 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); + }); + +}); diff --git a/dist/strap/pages/results.html b/dist/strap/pages/results.html index 243da5d..6ff4950 100644 --- a/dist/strap/pages/results.html +++ b/dist/strap/pages/results.html @@ -4,6 +4,7 @@ return; [/calc] [tmp page_title]__COMPANY__ -- [L]Search Results[/L][/tmp] +[tmpn display_class]leftonly[/tmpn] [if value more_link] [tmp page_title]__COMPANY__ -- [either][value-extended name=mv_searchspec joiner=" / "][or][value name=more_link filter=uri2string keep=1][/either][/tmp] [/if] @@ -13,13 +14,6 @@ [component]product_tree[/component] [output]left[/output] [/control-set] -[control-set] - [component]random[/component] - [banner][L]Specials[/L][/banner] - [cols]col-xs-6 col-sm-3 col-md-12[/cols] - [size]3[/size] - [output]right[/output] -[/control-set] [control reset=1] @_TOP_@ @@ -113,10 +107,10 @@ [if-item-field category] <h3>[either][value banner_text][or][item-field category][/either]</h3> [/if-item-field] - <div class="row"> + <div class="row equal-height-container"> [/item-change 1] - <div class="col-xs-6 col-sm-4 col-md-6 col-lg-3"> + <div class="col-xs-6 col-sm-4 col-md-3 col-lg-2" data-mh="[item-filter word][item-field category][/item-filter]"> <div class="thumbnail"> <a href="[area [item-code]]">[image sku="[item-code]" default="../thumb.gif" imagesubdir=items makesize="120x150>"]</a> <div class="caption"> diff --git a/dist/strap/templates/components/cross b/dist/strap/templates/components/cross index e1e22c1..6877f43 100644 --- a/dist/strap/templates/components/cross +++ b/dist/strap/templates/components/cross @@ -79,8 +79,8 @@ buttons: <h4 class="text-muted">[control name=banner default="[L]See also...[/L]"]</h4> [div-organize embed=lc pretty=1 cols="12" filler_class="[control cols col-sm-3]" row_attr='class="row equal-height-container"'] [loop random="[scratch random]" list="[strip interpolate=1][scratch cross_codes][/strip]"] - <div class="[control cols col-sm-3] equal-height-column"> - <DIV class="thumbnail equal-height-column"> + <div class="[control cols col-sm-3]"> + <DIV class="thumbnail"> <a href="[area [loop-code]]">[image src="thumb/[loop-field thumb]" default="thumb.gif"]</a> <DIV class="caption text-center"> <h5><a href="[area [loop-code]]">[loop-description]</a></h5> diff --git a/dist/strap/variables/JS b/dist/strap/variables/JS index d354f74..dbe5bf6 100644 --- a/dist/strap/variables/JS +++ b/dist/strap/variables/JS @@ -1,13 +1,9 @@ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> -<script src="[var WWW_DIR]/js/jquery.equalheightcolumns.min.js"></script> +<script src="[var WWW_DIR]/js/jquery.matchHeight.js"></script> <script> $(document).ready(function() { - $(".equal-height-container .equal-height-column").equalHeightColumns({ - maxWidth: 1199, - checkHeight: 'innerHeight' - }); -}); + $('.equal-height-container').matchHeight(); </script> [if var GOOGLE_ANL_ID]