MicroEvent.js is a event emitter library which provides the observer pattern to javascript objects. It works on node.js and browser. It is a single .js file containing a 20 lines class (only 321-bytes after minification+gzip).

/**
 * MicroEvent - to make any js object an event emitter
 *
 * - pure javascript - server compatible, browser compatible
 * - dont rely on the browser doms
 * - super simple - you get it immediatly, no mistery, no magic involved
 *
 * Also see: https://javascript.info/mixins#eventmixin
 *
 * @author Jerome Etienne (https://github.com/jeromeetienne)
 */

const MicroEvent = function() {};

MicroEvent.prototype = {
    on: function(event, fct) {
        this._events = this._events || {};
        this._events[event] = this._events[event] || [];
        this._events[event].push(fct);
    },

    off: function(event, fct) {
        const n = arguments.length;
        if (n === 0) return delete this._events;
        if (n === 1) return delete this._events[event];

        this._events = this._events || {};
        if (!event in this._events) return;
        this._events[event].splice(this._events[event].indexOf(fct), 1);
    },

    trigger: function(event /* , args... */) {
        this._events = this._events || {};
        if (!event in this._events) return;
        for (let i = 0; i < this._events[event].length; i++) {
            this._events[event][i].apply(
                this,
                Array.prototype.slice.call(arguments, 1)
            );
        }
    }
};

/**
 * Mixin will delegate all MicroEvent.js function in the destination object.
 *
 * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
 *
 * @static
 * @param {object} destObject the object which will support MicroEvent
 */
MicroEvent.mixin = function(destObject) {
    const props = ["on", "off", "trigger"];
    for (let i = 0; i < props.length; i++) {
        destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
    }
};

// ---------------------------------------------------------------------------
// Example Usage
// ---------------------------------------------------------------------------

const Ticker = function(interval) {
    const self = this;
    setInterval(function() {
        self.trigger("tick", new Date());
    }, interval);
};

/**
 * make Ticker support MicroEventjs
 */
MicroEvent.mixin(Ticker);

// create a ticker
const ticker = new Ticker(500);

// bind the 'tick' event
ticker.on("tick", function(date) {
    // display to check
    console.log("notified date: " + date);
});

//
// Class-ified/ES6 Version

class MicroEvent {
    /**
     * MicroEvent - to make any js object an event emitter
     *
     * - pure javascript - server compatible, browser compatible
     * - dont rely on the browser doms
     * - super simple - you get it immediatly, no mistery, no magic involved
     *
     * Also see: https://javascript.info/mixins#eventmixin
     *
     * @author Jerome Etienne (https://github.com/jeromeetienne)
     */
    constructor() {}

    on(event, fn) {
        this._events = this._events || {};
        this._events[event] = this._events[event] || [];
        this._events[event].push(fn);
    }

    off(event, fn) {
        const argsLength = arguments.length;
        if (argsLength === 0) {
            return delete this._events;
        }
        if (argsLength === 1) {
            return delete this._events[event];
        }

        this._events = this._events || {};
        if (!event in this._events) {
            return;
        }
        this._events[event].splice(this._events[event].indexOf(fn), 1);
    }

    trigger(event /* , args... */) {
        this._events = this._events || {};
        if (!event in this._events) {
            return;
        }

        for (let i = 0; i < this._events[event].length; i++) {
            this._events[event][i].apply(
                this,
                Array.prototype.slice.call(arguments, 1)
            );
        }
    }

    /**
     * Mixin will delegate all MicroEvent.js function in the destination object.
     *
     * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
     *
     * @static
     * @param {object} destObject the object which will support MicroEvent
     */
    static mixin(destObject) {
        const props = ["on", "off", "trigger"];
        for (let i = 0; i < props.length; i++) {
            destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
        }
    }
}

// ---------------------------------------------------------------------------
// Example Usage
// ---------------------------------------------------------------------------

const Ticker = function(interval) {
    const self = this;
    setInterval(() => {
        self.trigger("tick", new Date());
    }, interval);
};

/**
 * make Ticker support MicroEventjs
 */
MicroEvent.mixin(Ticker);

// create a ticker
const ticker = new Ticker(500);

// bind the 'tick' event
ticker.on("tick", date => {
    // display to check
    console.log("notified date: " + date);
});