I created a widget to handle multiple selections in CoffeeScript. There's a working attached snippet that uses the compiled JavaScript (Stack Snippets don't support CoffeeScript), which is available to see. There are two classes: DocList and Doc (I didn't use Document fearing a name clash).The DocList class keeps track of how many documents it has, how many have been selected. It handles displaying the right controls, selecting and deselecting all, and maintaining the state of its checkbox.class DocList  numOfcheckedBoxes: 0  numOfDocs: 0  constructor: (@list) ->    @docs = (new Doc(doc, @) for doc in @list.find('[data-behaviour~=doc]'))    @numOfDocs = @docs.length    @filters = @list.find '[data-behaviour~=documentListFilters]'    @actions = @list.find '[data-behaviour~=documentListActions]'    @showOrHideActions()    @selectAll = @list.find '[data-behaviour~=documentListSelectAll]'    # this displays how mant docs are selected to the user    @documentsSelected = @list.find '[data-behaviour=documentsSelected]'    @setEvents()  setEvents: ->    @selectAll.on 'click', =>      if @selectAll.prop 'checked'        @checkAll()      else        @uncheckAll()  checkAll: ->    doc.check() for doc in @docs    @updateSelectedCount @numOfDocs  uncheckAll: ->    doc.uncheck() for doc in @docs    @updateSelectedCount 0  handleCheckBoxEvent: (checked) ->    if checked      @updateSelectedCount @numOfcheckedBoxes + 1    else      @updateSelectedCount @numOfcheckedBoxes - 1  updateSelectedCount: (count) ->    @numOfcheckedBoxes = count    @documentsSelected.html #{@numOfcheckedBoxes} #{@documentsSelected.data 'label'}    if @numOfcheckedBoxes      if @numOfcheckedBoxes < @numOfDocs        @selectAll.prop 'indeterminate', true      else        @selectAll.prop 'indeterminate', false        @selectAll.prop 'checked', true    else      @selectAll.prop 'indeterminate', false      @selectAll.prop 'checked', false    @showOrHideActions()  showOrHideActions: ->    if @numOfcheckedBoxes      @filters.hide()      @actions.show()    else      @filters.show()      @actions.hide()The Doc class maintains a reference to its parent so that, when its checked, it can tell its parent it's been checked (to update counts and state).class Doc  constructor: (doc, @list) ->    @doc = $(doc)    @checkbox = @doc.find 'input[type=checkbox]'    @setEvents()  setEvents: ->    @checkbox.on 'click', =>      @handleClick()  handleClick: ->    checked = @checkbox.prop 'checked'    if checked      @doc.addClass 'document--selected'    else      @doc.removeClass 'document--selected'    @list.handleCheckBoxEvent(checked)  check: ->    @checkbox.prop 'checked', true    @handleClick()  uncheck: ->    @checkbox.prop 'checked', false    @handleClick()And here's the initialization:$(document).on 'ready', ->  new DocList $('[data-behaviour~=documentList]')Some notes:I don't like how the updateSelectedCount() works. Perhaps increment or decrement would be better here? But that might complicate other logic. Another issue is the conditionals to make sure the selectAll checkbox keeps a correct state (indeterminate, checked, etc...). It looks confusing. I like how I'm handling dependency injection by passing the list to its child doc, but is there a better way?Some additional notes. If this were Ruby, the checkAll(), uncheckAll(), and updateSelectedCount() in the DocList class would be private methods. As well as handleClick() in the Doc class. Alas, it doesn't seem that there's an easy way to define and use private methods in CoffeeScript, at at least it's beyond my understanding.var Doc, DocList;DocList = (function() {  DocList.prototype.numOfcheckedBoxes = 0;  DocList.numOfDocs = 0;  function DocList(list) {    var doc;    this.list = list;    this.docs = (function() {      var i, len, ref, results;      ref = this.list.find('[data-behaviour~=doc]');      results = [];      for (i = 0, len = ref.length; i < len; i++) {        doc = ref[i];        results.push(new Doc(doc, this));      }      return results;    }).call(this);    this.numOfDocs = this.docs.length;    this.filters = this.list.find('[data-behaviour~=documentListFilters]');    this.actions = this.list.find('[data-behaviour~=documentListActions]');    this.showOrHideActions();    this.selectAll = this.list.find('[data-behaviour~=documentListSelectAll]');    this.documentsSelected = this.list.find('[data-behaviour=documentsSelected]');    this.setEvents();    console.log('inited');  }  DocList.prototype.setEvents = function() {    return this.selectAll.on('click', (function(_this) {      return function() {        if (_this.selectAll.prop('checked')) {          return _this.checkAll();        } else {          return _this.uncheckAll();        }      };    })(this));  };  DocList.prototype.checkAll = function() {    var doc, i, len, ref;    ref = this.docs;    for (i = 0, len = ref.length; i < len; i++) {      doc = ref[i];      doc.check();    }    return this.updateSelectedCount(this.numOfDocs);  };  DocList.prototype.uncheckAll = function() {    var doc, i, len, ref;    ref = this.docs;    for (i = 0, len = ref.length; i < len; i++) {      doc = ref[i];      doc.uncheck();    }    return this.updateSelectedCount(0);  };  DocList.prototype.handleCheckBoxEvent = function(checked) {    if (checked) {      return this.updateSelectedCount(this.numOfcheckedBoxes + 1);    } else {      return this.updateSelectedCount(this.numOfcheckedBoxes - 1);    }  };  DocList.prototype.updateSelectedCount = function(count) {    this.numOfcheckedBoxes = count;    this.documentsSelected.html(this.numOfcheckedBoxes +   + (this.documentsSelected.data('label')));    if (this.numOfcheckedBoxes) {      if (this.numOfcheckedBoxes < this.numOfDocs) {        this.selectAll.prop('indeterminate', true);      } else {        this.selectAll.prop('indeterminate', false);        this.selectAll.prop('checked', true);      }    } else {      this.selectAll.prop('indeterminate', false);      this.selectAll.prop('checked', false);    }    return this.showOrHideActions();  };  DocList.prototype.showOrHideActions = function() {    if (this.numOfcheckedBoxes) {      this.filters.hide();      return this.actions.show();    } else {      this.filters.show();      return this.actions.hide();    }  };  return DocList;})();Doc = (function() {  function Doc(doc, list) {    this.list = list;    this.doc = $(doc);    this.checkbox = this.doc.find('input[type=checkbox]');    this.setEvents();  }  Doc.prototype.setEvents = function() {    return this.checkbox.on('click', (function(_this) {      return function() {        return _this.handleClick();      };    })(this));  };  Doc.prototype.handleClick = function() {    var checked;    checked = this.checkbox.prop('checked');    if (checked) {      this.doc.addClass('document--selected');    } else {      this.doc.removeClass('document--selected');    }    return this.list.handleCheckBoxEvent(checked);  };  Doc.prototype.check = function() {    this.checkbox.prop('checked', true);    return this.handleClick();  };  Doc.prototype.uncheck = function() {    this.checkbox.prop('checked', false);    return this.handleClick();  };  return Doc;})();$(document).on('ready', function() {  return new DocList($('[data-behaviour~=documentList]'));});// ---// generated by coffee-script 1.9.2label {  margin-right: 10px;}<link href=//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css rel=stylesheet/><script src=https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js></script><div class=panel panel-default data-behaviour=documentList>  <div class=panel-heading>    <ul class=list-inline data-behaviour=documentListFilters>      <li>Order by</li>      <li><a href=#>Recent</a></li>      <li><a href=#>Oldest</a></li>    </ul>    <ul class=list-inline data-behaviour=documentListActions>      <li><input type=checkbox data-behaviour=documentListSelectAll /></li>      <li><strong data-behaviour=documentsSelected data-label=selected></strong></li>      <li><a href=#>Change Status</a></li>      <li><a href=#>Change Phase</a></li>    </ul>  </div>  <ul class=list-group>    <li data-behaviour=doc class=list-group-item>      <label class=pull-left>        <input type=checkbox />       </label>      <a class=text-link href=AEROGAMI.png>AEROGAMI-1.png</a>    </li>    <li data-behaviour=doc class=list-group-item>      <label class=pull-left>        <input type=checkbox />       </label>      <a class=text-link href=AEROGAMI.png>AEROGAMI-1.png</a>    </li>    <li data-behaviour=doc class=list-group-item>      <label class=pull-left>        <input type=checkbox />       </label>      <a class=text-link href=AEROGAMI.png>AEROGAMI-1.png</a>    </li>  </ul></div>
Multiple select widget in CoffeeScript
coffeescript;user interface