0

I have a number of different "control elements" on my application: dropdowns, tabs, menus, etc. On same pages, there are many of the same control. When writing JavaScript to handle the different events associated with each of these controls, I'm trying to make my code as DRY as possible. One of the challenges is modularizing my JQuery code so that events that occur within a specific control only effect that control.

Take this initial code for example, all it does is open a dropdown menu when it is clicked. I'm used to writing just a ton of different anonymous functions triggered by different events so this type of JQuery is really new to me.

var dropdown = {
  init: function() {
    $(".dropdown").click(".dropdown", dropdown.openDropdown);
  },  
  openDropdown: function() {
    $(this).children(".dropdown-menu").show();
    $(this).addClass("open");
  }
}

$(document).ready(dropdown.init);

My question is, within this dropdown variable, I want to be able to save/track different pieces of the dropdown control currently being acted upon. For example, I might want to write:

var menu = $(this).children(".dropdown-menu");

somewhere in this chunk so that I could refer back to this menu while calling different functions. I just cannot figure out syntactically how to do this. Any help/guidance is welcomed! Thanks.

4

3 回答 3

3

Something I like about coffeescript is how it allows you to easily create classes. Classes in coffee are just a simplified way of generating "modules" using javascript's prototypal inheritance. More on that here: http://coffeescript.org/#classes

But how YOU could implement more modular jQuery code is by doing something like this:

http://jsfiddle.net/x858q/2/

var DropDown = (function(){
    // constructor
    function DropDown(el){
        this.el = $(el);
        this.link = this.el.find("a");
        this.menu = this.el.find(".dropdown-menu");
        this.bindClick();
    }

    // method binding click event listener
    DropDown.prototype.bindClick = function(){
        var _this = this;
        this.link.click(function(e){
            _this.openDropDown();
            e.preventDefault();
        });
    };

    // click event handler
    DropDown.prototype.openDropDown = function(){
        this.menu.show();
        this.link.addClass("open");
    };

    return DropDown;
})();

$(function(){
    // init each .dropdown element as a new DropDown
    $(".dropdown").each(function(){
       new DropDown(this); 
    });
});
于 2014-05-23T20:09:24.790 回答
1

You've touched on a pattern I've been leaning towards more and more. Basically, create a JavaScript object that acts as a controller given a root element on the page. Since this "dropdown" is pretty generic, it could probably have access to the whole page and be perfectly happy. I would also recommend making these "modules" instantiable objects, as this allows you to write unit tests easier:

function DropdownModule() {
    this.handleClick = this.handleClick.bind(this);
}

DropdownModule.prototype = {

    element: null,

    $element: null

    constructor: DropdownModule,

    init: function(element) {
        this.setElement(element);
        this.$element.on("click", ".dropdown", this.handleClick);
    },

    handleClick: function(event) {
        var $dropdown = $(event.currentTarget);
        $dropdown.children(".dropdown-menu").show();
        $dropdown.addClass("open");

        this.someOtherFunction($dropdown);
    },

    someOtherFunction($dropdown) {
        // do something with $dropdown
    },

    setElement: function(element) {
        this.element = element;
        this.$element = $(element);
    }

}

Then to use it, just throw this anywhere after the definition for Dropdown:

var dropdown = new Dropdown()
    .init(document.documentElement);

The document.documentElement property refers to the <html> tag and is available the moment JavaScript begins executing.

As a side note, I've built a whole framework around this approach: Foundry. Other frameworks, like Angular, take a similar approach as well.

于 2014-05-23T20:04:39.067 回答
1

What you want sounds like exactly what jQuery UI has already implemented in their Widget Factory.

I'd highly recommend you check it out since what you'd end up with it something like

$.widget( 'dropdown', {
  _create: function() {
    this.element.addClass( 'dropdown' );

    this._on({
      'click': '_clicked'
    });
  },

  _clicked: function( event ) {
    // `this` is an instance of dropdown here, not the element
    this.clicked = !this.clicked;

    this.element.toggleClass( 'clicked', this.clicked );  
  },

  _destroy: function() {
    this.element.removeClass( 'dropdown' );
  }
});

Then you would use it like any other jQuery UI Widget

$( '#some-element' ).dropdown();
于 2014-05-23T21:45:26.253 回答