123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- 'use strict';
- import $ from 'jquery';
- import { MediaQuery } from './foundation.util.mediaQuery';
- import { onImagesLoaded } from './foundation.util.imageLoader';
- import { GetYoDigits } from './foundation.core.utils';
- import { Plugin } from './foundation.core.plugin';
- /**
- * Equalizer module.
- * @module foundation.equalizer
- * @requires foundation.util.mediaQuery
- * @requires foundation.util.imageLoader if equalizer contains images
- */
- class Equalizer extends Plugin {
- /**
- * Creates a new instance of Equalizer.
- * @class
- * @name Equalizer
- * @fires Equalizer#init
- * @param {Object} element - jQuery object to add the trigger to.
- * @param {Object} options - Overrides to the default plugin settings.
- */
- _setup(element, options){
- this.$element = element;
- this.options = $.extend({}, Equalizer.defaults, this.$element.data(), options);
- this.className = 'Equalizer'; // ie9 back compat
- this._init();
- }
- /**
- * Initializes the Equalizer plugin and calls functions to get equalizer functioning on load.
- * @private
- */
- _init() {
- var eqId = this.$element.attr('data-equalizer') || '';
- var $watched = this.$element.find(`[data-equalizer-watch="${eqId}"]`);
- MediaQuery._init();
- this.$watched = $watched.length ? $watched : this.$element.find('[data-equalizer-watch]');
- this.$element.attr('data-resize', (eqId || GetYoDigits(6, 'eq')));
- this.$element.attr('data-mutate', (eqId || GetYoDigits(6, 'eq')));
- this.hasNested = this.$element.find('[data-equalizer]').length > 0;
- this.isNested = this.$element.parentsUntil(document.body, '[data-equalizer]').length > 0;
- this.isOn = false;
- this._bindHandler = {
- onResizeMeBound: this._onResizeMe.bind(this),
- onPostEqualizedBound: this._onPostEqualized.bind(this)
- };
- var imgs = this.$element.find('img');
- var tooSmall;
- if(this.options.equalizeOn){
- tooSmall = this._checkMQ();
- $(window).on('changed.zf.mediaquery', this._checkMQ.bind(this));
- }else{
- this._events();
- }
- if((typeof tooSmall !== 'undefined' && tooSmall === false) || typeof tooSmall === 'undefined'){
- if(imgs.length){
- onImagesLoaded(imgs, this._reflow.bind(this));
- }else{
- this._reflow();
- }
- }
- }
- /**
- * Removes event listeners if the breakpoint is too small.
- * @private
- */
- _pauseEvents() {
- this.isOn = false;
- this.$element.off({
- '.zf.equalizer': this._bindHandler.onPostEqualizedBound,
- 'resizeme.zf.trigger': this._bindHandler.onResizeMeBound,
- 'mutateme.zf.trigger': this._bindHandler.onResizeMeBound
- });
- }
- /**
- * function to handle $elements resizeme.zf.trigger, with bound this on _bindHandler.onResizeMeBound
- * @private
- */
- _onResizeMe(e) {
- this._reflow();
- }
- /**
- * function to handle $elements postequalized.zf.equalizer, with bound this on _bindHandler.onPostEqualizedBound
- * @private
- */
- _onPostEqualized(e) {
- if(e.target !== this.$element[0]){ this._reflow(); }
- }
- /**
- * Initializes events for Equalizer.
- * @private
- */
- _events() {
- var _this = this;
- this._pauseEvents();
- if(this.hasNested){
- this.$element.on('postequalized.zf.equalizer', this._bindHandler.onPostEqualizedBound);
- }else{
- this.$element.on('resizeme.zf.trigger', this._bindHandler.onResizeMeBound);
- this.$element.on('mutateme.zf.trigger', this._bindHandler.onResizeMeBound);
- }
- this.isOn = true;
- }
- /**
- * Checks the current breakpoint to the minimum required size.
- * @private
- */
- _checkMQ() {
- var tooSmall = !MediaQuery.is(this.options.equalizeOn);
- if(tooSmall){
- if(this.isOn){
- this._pauseEvents();
- this.$watched.css('height', 'auto');
- }
- }else{
- if(!this.isOn){
- this._events();
- }
- }
- return tooSmall;
- }
- /**
- * A noop version for the plugin
- * @private
- */
- _killswitch() {
- return;
- }
- /**
- * Calls necessary functions to update Equalizer upon DOM change
- * @private
- */
- _reflow() {
- if(!this.options.equalizeOnStack){
- if(this._isStacked()){
- this.$watched.css('height', 'auto');
- return false;
- }
- }
- if (this.options.equalizeByRow) {
- this.getHeightsByRow(this.applyHeightByRow.bind(this));
- }else{
- this.getHeights(this.applyHeight.bind(this));
- }
- }
- /**
- * Manually determines if the first 2 elements are *NOT* stacked.
- * @private
- */
- _isStacked() {
- if (!this.$watched[0] || !this.$watched[1]) {
- return true;
- }
- return this.$watched[0].getBoundingClientRect().top !== this.$watched[1].getBoundingClientRect().top;
- }
- /**
- * Finds the outer heights of children contained within an Equalizer parent and returns them in an array
- * @param {Function} cb - A non-optional callback to return the heights array to.
- * @returns {Array} heights - An array of heights of children within Equalizer container
- */
- getHeights(cb) {
- var heights = [];
- for(var i = 0, len = this.$watched.length; i < len; i++){
- this.$watched[i].style.height = 'auto';
- heights.push(this.$watched[i].offsetHeight);
- }
- cb(heights);
- }
- /**
- * Finds the outer heights of children contained within an Equalizer parent and returns them in an array
- * @param {Function} cb - A non-optional callback to return the heights array to.
- * @returns {Array} groups - An array of heights of children within Equalizer container grouped by row with element,height and max as last child
- */
- getHeightsByRow(cb) {
- var lastElTopOffset = (this.$watched.length ? this.$watched.first().offset().top : 0),
- groups = [],
- group = 0;
- //group by Row
- groups[group] = [];
- for(var i = 0, len = this.$watched.length; i < len; i++){
- this.$watched[i].style.height = 'auto';
- //maybe could use this.$watched[i].offsetTop
- var elOffsetTop = $(this.$watched[i]).offset().top;
- if (elOffsetTop!=lastElTopOffset) {
- group++;
- groups[group] = [];
- lastElTopOffset=elOffsetTop;
- }
- groups[group].push([this.$watched[i],this.$watched[i].offsetHeight]);
- }
- for (var j = 0, ln = groups.length; j < ln; j++) {
- var heights = $(groups[j]).map(function(){ return this[1]; }).get();
- var max = Math.max.apply(null, heights);
- groups[j].push(max);
- }
- cb(groups);
- }
- /**
- * Changes the CSS height property of each child in an Equalizer parent to match the tallest
- * @param {array} heights - An array of heights of children within Equalizer container
- * @fires Equalizer#preequalized
- * @fires Equalizer#postequalized
- */
- applyHeight(heights) {
- var max = Math.max.apply(null, heights);
- /**
- * Fires before the heights are applied
- * @event Equalizer#preequalized
- */
- this.$element.trigger('preequalized.zf.equalizer');
- this.$watched.css('height', max);
- /**
- * Fires when the heights have been applied
- * @event Equalizer#postequalized
- */
- this.$element.trigger('postequalized.zf.equalizer');
- }
- /**
- * Changes the CSS height property of each child in an Equalizer parent to match the tallest by row
- * @param {array} groups - An array of heights of children within Equalizer container grouped by row with element,height and max as last child
- * @fires Equalizer#preequalized
- * @fires Equalizer#preequalizedrow
- * @fires Equalizer#postequalizedrow
- * @fires Equalizer#postequalized
- */
- applyHeightByRow(groups) {
- /**
- * Fires before the heights are applied
- */
- this.$element.trigger('preequalized.zf.equalizer');
- for (var i = 0, len = groups.length; i < len ; i++) {
- var groupsILength = groups[i].length,
- max = groups[i][groupsILength - 1];
- if (groupsILength<=2) {
- $(groups[i][0][0]).css({'height':'auto'});
- continue;
- }
- /**
- * Fires before the heights per row are applied
- * @event Equalizer#preequalizedrow
- */
- this.$element.trigger('preequalizedrow.zf.equalizer');
- for (var j = 0, lenJ = (groupsILength-1); j < lenJ ; j++) {
- $(groups[i][j][0]).css({'height':max});
- }
- /**
- * Fires when the heights per row have been applied
- * @event Equalizer#postequalizedrow
- */
- this.$element.trigger('postequalizedrow.zf.equalizer');
- }
- /**
- * Fires when the heights have been applied
- */
- this.$element.trigger('postequalized.zf.equalizer');
- }
- /**
- * Destroys an instance of Equalizer.
- * @function
- */
- _destroy() {
- this._pauseEvents();
- this.$watched.css('height', 'auto');
- }
- }
- /**
- * Default settings for plugin
- */
- Equalizer.defaults = {
- /**
- * Enable height equalization when stacked on smaller screens.
- * @option
- * @type {boolean}
- * @default false
- */
- equalizeOnStack: false,
- /**
- * Enable height equalization row by row.
- * @option
- * @type {boolean}
- * @default false
- */
- equalizeByRow: false,
- /**
- * String representing the minimum breakpoint size the plugin should equalize heights on.
- * @option
- * @type {string}
- * @default ''
- */
- equalizeOn: ''
- };
- export {Equalizer};
|