Menu icon Foundation
Extending Foundation JS Components - Multiple Equalizers

I've come across a situation where I need to duplicate foundation's equalizer component so I can use it on two groups that need to be equalized in the same element. (See markup below)

As you can see, I'm equalizing all of the top level a tags and the un-ordered lists separately.

<!--Main Navigation-->
<nav role="navigation" id="uthsc-navigation-alt" class="show-for-large-up">
    <ul class="row collapse" data-equalizer data-uthscequalizer>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Biomedical Education</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Molecular Sciences</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Faculty & Staff</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown link in dropdow link in dropdow</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Neuro Scientific</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Dropdown 5</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Dropdown 6</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>
    </ul>
</nav>
            

         

Since this functionality is not possible by default, my solution was to completely duplicate foundation.equalizer.js, rename every instance of 'equalizer' with 'uthscequalizer.' and add that in to foundation.js, just below equalizer.

(function ($, window, document, undefined) {
  'use strict';

  Foundation.libs.uthscequalizer = {
    name : 'uthscequalizer',

    version : '5.4.7',

    settings : {
      use_tallest: true,
      before_height_change: $.noop,
      after_height_change: $.noop,
      equalize_on_stack: false
    },

    init : function (scope, method, options) {
      Foundation.inherit(this, 'image_loaded');
      this.bindings(method, options);
      this.reflow();
    },

    events : function () {
      this.S(window).off('.uthscequalizer').on('resize.fndtn.uthscequalizer', function(e){
        this.reflow();
      }.bind(this));
    },

    equalize: function(uthscequalizer) {
      var isStacked = false,
          vals = uthscequalizer.find('[' + this.attr_name() + '-watch]:visible'),
          settings = uthscequalizer.data(this.attr_name(true)+'-init');

      if (vals.length === 0) return;
      var firstTopOffset = vals.first().offset().top;
      settings.before_height_change();
      uthscequalizer.trigger('before-height-change').trigger('before-height-change.fndth.uthscequalizer');
      vals.height('inherit');
      vals.each(function(){
        var el = $(this);
        if (el.offset().top !== firstTopOffset) {
          isStacked = true;
        }
      });

      if (settings.equalize_on_stack === false) {
        if (isStacked) return;
      };

      var heights = vals.map(function(){ return $(this).outerHeight(false) }).get();

      if (settings.use_tallest) {
        var max = Math.max.apply(null, heights);
        vals.css('height', max);
      } else {
        var min = Math.min.apply(null, heights);
        vals.css('height', min);
      }
      settings.after_height_change();
      uthscequalizer.trigger('after-height-change').trigger('after-height-change.fndtn.uthscequalizer');
    },

    reflow : function () {
      var self = this;

      this.S('[' + this.attr_name() + ']', this.scope).each(function(){
        var $eq_target = $(this);
        self.image_loaded(self.S('img', this), function(){
          self.equalize($eq_target)
        });
      });
    }
  };
})(jQuery, window, window.document);


So, this works perfectly but it's obviously not the right way to do things. I'd like to include this outside of foundation's core files. What should I do?

The ideal solution, here, would be to allow equalizers to be name-spaced so that they could be used in multiple places on a page or in an element.

jsEqualizercomponents

I've come across a situation where I need to duplicate foundation's equalizer component so I can use it on two groups that need to be equalized in the same element. (See markup below)

As you can see, I'm equalizing all of the top level a tags and the un-ordered lists separately.

<!--Main Navigation-->
<nav role="navigation" id="uthsc-navigation-alt" class="show-for-large-up">
    <ul class="row collapse" data-equalizer data-uthscequalizer>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Biomedical Education</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Molecular Sciences</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Faculty & Staff</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown link in dropdow link in dropdow</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Neuro Scientific</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Dropdown 5</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>

        <li class="uthsc-navigation-column small-2 columns">
            <a href="#" data-equalizer-watch>Dropdown 6</a>
            <ul data-uthscequalizer-watch>
                <li><a href="#">First link in dropdown</a></li>
                <li class="active"><a href="#">Active link in dropdown</a></li>
            </ul>
        </li>
    </ul>
</nav>
            

         

Since this functionality is not possible by default, my solution was to completely duplicate foundation.equalizer.js, rename every instance of 'equalizer' with 'uthscequalizer.' and add that in to foundation.js, just below equalizer.

(function ($, window, document, undefined) {
  'use strict';

  Foundation.libs.uthscequalizer = {
    name : 'uthscequalizer',

    version : '5.4.7',

    settings : {
      use_tallest: true,
      before_height_change: $.noop,
      after_height_change: $.noop,
      equalize_on_stack: false
    },

    init : function (scope, method, options) {
      Foundation.inherit(this, 'image_loaded');
      this.bindings(method, options);
      this.reflow();
    },

    events : function () {
      this.S(window).off('.uthscequalizer').on('resize.fndtn.uthscequalizer', function(e){
        this.reflow();
      }.bind(this));
    },

    equalize: function(uthscequalizer) {
      var isStacked = false,
          vals = uthscequalizer.find('[' + this.attr_name() + '-watch]:visible'),
          settings = uthscequalizer.data(this.attr_name(true)+'-init');

      if (vals.length === 0) return;
      var firstTopOffset = vals.first().offset().top;
      settings.before_height_change();
      uthscequalizer.trigger('before-height-change').trigger('before-height-change.fndth.uthscequalizer');
      vals.height('inherit');
      vals.each(function(){
        var el = $(this);
        if (el.offset().top !== firstTopOffset) {
          isStacked = true;
        }
      });

      if (settings.equalize_on_stack === false) {
        if (isStacked) return;
      };

      var heights = vals.map(function(){ return $(this).outerHeight(false) }).get();

      if (settings.use_tallest) {
        var max = Math.max.apply(null, heights);
        vals.css('height', max);
      } else {
        var min = Math.min.apply(null, heights);
        vals.css('height', min);
      }
      settings.after_height_change();
      uthscequalizer.trigger('after-height-change').trigger('after-height-change.fndtn.uthscequalizer');
    },

    reflow : function () {
      var self = this;

      this.S('[' + this.attr_name() + ']', this.scope).each(function(){
        var $eq_target = $(this);
        self.image_loaded(self.S('img', this), function(){
          self.equalize($eq_target)
        });
      });
    }
  };
})(jQuery, window, window.document);


So, this works perfectly but it's obviously not the right way to do things. I'd like to include this outside of foundation's core files. What should I do?

The ideal solution, here, would be to allow equalizers to be name-spaced so that they could be used in multiple places on a page or in an element.

George S almost 5 years ago

So here's how I fixed this. It's still probably not the ideal solution but it's good enough.

I actually used grunt to concatenate my uthscequalizer partial on to the end of the concatenated foundation.js file and place it in my js folder as uthsc.foundation.js

As part of the same task, I minify (uglify) that file in to uthsc.foundation.min.js

So at the end of the day, instead of pointing to the foundation.min.js file, I point to the uthsc.foundation.min.js file generated by grunt.

I separated the foundation tasks from the default tasks so that they don't run as part of the default build and watch tasks.

Here's my Grunt file
```
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),

    sass: {
        options: {
            includePaths: ['bower_components/foundation/scss']
        },
        dist: {
            options: {
                outputStyle: 'compressed'
            },
            files: {
                'css/uthsc.css': 'scss/uthsc.scss'
            }
        }
    },

    concat: {
        options: {
            separator: '\n'
        },

        dist: {
            src: ['js/scripts/*.js'],
            dest: 'js/uthsc.js'
        },

        foundation: {
            src: ['bower_components/foundation/js/foundation.js','js/scripts/uthsc.equalizer.js'],
            dest: 'js/uthsc.foundation.js'
        }
    },

    uglify: {
        options: {
            mangle: {
                except: ['jQuery', 'Backbone']
            }
        },

        dist: {
            files: {
                'js/uthsc.min.js': ['js/uthsc.js']
            }
        },

        foundation: {
            files: {
                'js/uthsc.foundation.min.js': ['js/uthsc.foundation.js']
            }
        }
    },

    watch: {
        grunt: {files: ['Gruntfile.js']},

        sass: {
            files: 'scss/**/*.scss',
            tasks: ['sass']
        },

        uglify: {
            files: 'js/scripts/**/*.js',
            tasks: ['concat','uglify']
        }
    }
});

//load tasks
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');

//foundation
grunt.registerTask('foundationConcatenate', ['concat:foundation']);
grunt.registerTask('foundationMinify', ['uglify:foundation']);
grunt.registerTask('foundation', ['foundationConcatenate','foundationMinify']);

//default
grunt.registerTask('concatenate', ['concat:dist']);
grunt.registerTask('minify', ['uglify:dist']);
grunt.registerTask('build', ['sass', 'concatenate', 'minify']);
grunt.registerTask('default', ['build', 'watch']);

};