JavaScript is one of the most powerful languages. It supports wide range of the programming styles and techniques, but such flexibility comes with danger. It is relatively easy for the JavaScript project to become a cluttered mess when the best practices are not followed or design patterns are applied incorrectly.

My goal for this article is to demonstrate how to apply the Model-View-Controller pattern while developing a simple JavaScript component. The component is the ListBox ("select" HTML tag) control with an editable list of items: the user should be able to select and remove items and add new items into the list.



  • Model - The domain-specific representation of the information on which the application operates. The model is another name for the domain layer. Domain logic adds meaning to raw data (e.g., calculating if today is the user's birthday, or the totals, taxes and shipping charges for shopping cart items).


Before implementing the MVC classes, the notification approach is need to be chosen. In JavaScript there is no special language construction or recommended interface on how the Publish-Subscribe pattern should be implemented. All implementations just use the function references. Most common of them is probably the EventEmitter implementation, which can be found in node.js for example. Two main methods of this implementations are "on" for adding event handler and "emit" for calling the event handlers for the specified event. The notifications can be added to the class by extending it from the EventEmitter or by adding methods that proxify calls to the internal instance of the EventEmitter.

class EventEmitter {
  constructor() {
    this._events = {};
  on(evt, listener) {
    (this._events[evt] || (this._events[evt] = [])).push(listener);
    return this;
  emit(evt, arg) {
    (this._events[evt] || []).slice().forEach(lsn => lsn(arg));


 * The Model. Model stores items and notifies
 * observers about changes.
class ListModel extends EventEmitter {
  constructor(items) {
    this._items = items || [];
    this._selectedIndex = -1;

  getItems() {
    return this._items.slice();

  addItem(item) {
    this.emit('itemAdded', item);

  removeItemAt(index) {
    const item = this._items.splice(index, 1)[0];
    this.emit('itemRemoved', item);
    if (index === this._selectedIndex) {
      this.selectedIndex = -1;

  get selectedIndex () {
    return this._selectedIndex;

  set selectedIndex(index) {
    const previousIndex = this._selectedIndex;
    this._selectedIndex = index;
    this.emit('selectedIndexChanged', previousIndex);


Here are the View and Controller classes:

 * The View. View presents the model and provides
 * the UI events. The controller is attached to these
 * events to handle the user interaction.
class ListView extends EventEmitter {
  constructor(model, elements) {
    this._model = model;
    this._elements = elements;

    // attach model listeners
    model.on('itemAdded', () => this.rebuildList())
      .on('itemRemoved', () => this.rebuildList());

    // attach listeners to HTML controls
      e => this.emit('listModified',;
      () => this.emit('addButtonClicked'));
      () => this.emit('delButtonClicked'));

  show() {

  rebuildList() {
    const list = this._elements.list;
    list.options.length = 0;
      item => list.options.add(new Option(item)));
    this._model.selectedIndex = -1;

 * The Controller. Controller responds to user actions and
 * invokes changes on the model.
class ListController {
  constructor(model, view) {
    this._model = model;
    this._view = view;

    view.on('listModified', idx => this.updateSelected(idx));
    view.on('addButtonClicked', () => this.addItem());
    view.on('delButtonClicked', () => this.delItem());

  addItem() {
    const item = window.prompt('Add item:', '');
    if (item) {

  delItem() {
    const index = this._model.selectedIndex;
    if (index !== -1) {

  updateSelected(index) {
    this._model.selectedIndex = index;


Your favourite JavaScript technologies:<br>
<select id="list" size="10" style="width: 17em"></select><br>
window.addEventListener('load', () => {
  const model = new ListModel(['node.js', 'react']),
    view = new ListView(model, {
      'list' : document.getElementById('list'),
      'addButton' : document.getElementById('plusBtn'), 
      'delButton' : document.getElementById('minusBtn')
    controller = new ListController(model, view);;
Your favourite JavaScript technologies: