Skip to content

Instantly share code, notes, and snippets.

@eprothro
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eprothro/de0cb76631ccfb40b4a9 to your computer and use it in GitHub Desktop.
Save eprothro/de0cb76631ccfb40b4a9 to your computer and use it in GitHub Desktop.
Simple Object Oriented JS Example

Object Oriented Javascript Example

Consider a simple HTML element that we want to expand and contract by tapping a different target element.

<a href='expandable-element-1' data-expander>
  <!-- some link markup styled using an `expanded` class -->
</a>

<!-- unknown number of elements -->

<div id='expandable-element-1'>
  <!-- some markup -->
<div>

This could be (and usually is) accomplished quite easily with a few tightly coupled JavaScript methods, defined anonymously and/or globally.

$('[data-expander]').on('click', function(e){
  var $trigger = $(this);
  var $target = $($trigger.attr('href'));
  e.preventDefault();

  if($target.is(":visible")){
    $target.slideUp('slow', function(){
      $trigger.removeClass('expanded');
    });
  }else{
    $target.slideDown('fast', function(){
      $trigger.addClass('expanded');
    });
  }
});

This can also be achieved with an object oriented design, using fundamental JS constructs (e.g. no additional dependencies). This allows a single 'class' to encapsulate all the responsibility for the JavaScript associated with this element.

With such a design, the cost of future change is drastically improved compared to a pile of procedural JavaScript. This is especially true when considering a real-world project as a whole, with dozens of elements all with often overlapping needs.

There are, of course, many optimizations and best practices available and recommended (e.g. settings/options injection and defaults, prototype inheritance, firing custom events, etc.). Below is a concise, though real-world, example that provides a foundation for future changes such as these, and others.

Note: the self variable is used as a convenience to reduce common confusion around the this keyword.

// assets/javascripts/elements/_expander.js

function Expander($el) {
  var self = this;

  self.$trigger      = $el;
  self.$target       = $el.attr('href');
  self.activeClass   = 'expanded';

  self.init();
}

Expander.prototype = {
  constructor : Expander,

    init : function() {
      var self = this;
      console.log('Expander initialized');

      // ensure expansion class matches initial state
      if(self.isExpanded()){
        self.$trigger.addClass(self.activeClass);
      }

      // register to expand or contract on target click
      self.$trigger.on('click', function(e){
        e.preventDefault();

        if(self.isExpanded()){
          self.contract();
        }else{
          self.expand();
        }
      });
    },
    expand : function() {
      var self = this;

      self.$target.slideDown('fast', function(){
        self.$trigger.addClass(self.activeClass);
      });
    },
    contract : function() {
      var self = this;

      self.$target.slideUp('slow', function(){
        self.$trigger.removeClass(self.activeClass);
      });
    },
    isExpanded : function() {
      var self = this;

      return self.$target.is(":visible");
    }
}

$(window).load(function(){
  // initialize objects for each expander available on the page
  $('[data-expander]').each(function(){
    var expander = new Expander($(this));
  });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment