Skip to content

Chapter 10: Prototype, MooTools, Dojo, and the Search for Structure

Selectors and event handlers can take a page a long way. They can’t take it all the way to an application.

The previous chapter described jQuery as the library that made the DOM ergonomic problem disappear for most working developers. Ergonomic DOM access is, on its own, a thin foundation for the kinds of applications people were starting to build by the mid-2000s. A dashboard with date filters, charts, modals, tabs, inline editing, notifications, permission checks, and a saved-state lifecycle is not just a long sequence of $().on() calls. It needs structure. It needs named units of behavior. It needs a way to write and reuse classes when JavaScript didn’t have classes. It needs a way to write and reuse modules when JavaScript didn’t have modules. It needs lifecycle, inheritance, namespacing, and a story for how parts of the page coordinate without stepping on each other.

Several libraries in jQuery’s generation tried to provide all of that. This chapter is about them.

The three names worth knowing are Dojo, Prototype, and MooTools. YUI, Spry, and a handful of smaller efforts ran alongside them. Each library had its own design philosophy, its own community, and its own architectural ambitions. Each one was, in some way, trying to give browser JavaScript the kind of structural discipline that server-side languages already had. None of them won the way jQuery did. Each one contributed ideas that the field eventually absorbed.

Dojo is the oldest of the major libraries in this chapter. The project began in 2004, led by Alex Russell, Dylan Schiemann, James Burke, and a small founding team that turned into the Dojo Foundation in 2005.

The project’s ambition was different from jQuery’s. Dojo wasn’t trying to make CSS-selector access ergonomic. It was trying to be a toolkit — a comprehensive set of utilities, widgets, build tools, module loaders, internationalization helpers, accessibility helpers, layout systems, data stores, and animation frameworks that, taken together, gave a team everything it needed to build a serious browser-based application. The closest parallel is the Java Swing toolkit on the desktop, or Cocoa on macOS, scaled to fit a browser.

Dojo’s design choices reflected this ambition. The library was organized into modules (dojo.core, dojo.io, dijit for widgets, dojox for experimental additions). It included a module loader long before ES modules existed in the language. It shipped a build tool that could combine the modules an application used into a single minified bundle, twenty years before Webpack and Vite would do the same thing.

Dijit, Dojo’s widget library, was particularly significant. Dijit shipped accessible, themeable, internationalized versions of the common interactive widgets a serious application needed — buttons, dialogs, trees, grids, date pickers, tab containers, accordion panels, form controls. Each Dijit widget was a JavaScript class with a defined lifecycle (postCreate, startup, destroy), a templating system, and an API for declaration both in JavaScript and in HTML (<div data-dojo-type="dijit/form/Button">). The HTML-as-declarative-syntax pattern is one Dojo got right early — the same pattern that custom elements would standardize fifteen years later.

The team had institutional weight. Russell would later move to Google and then to Microsoft, where he became one of the most vocal advocates for browser platform improvements (custom elements, container queries, the View Transitions API, and many others all owe pieces of their design to Russell’s work). Burke wrote RequireJS, the AMD module loader that became one of the dominant pre-ES6 module systems. Schiemann founded SitePen, the consultancy that funded much of Dojo’s later development. The library was less popular than jQuery, but it shaped the platform in durable ways.

Dojo’s commercial trajectory is worth noting. The library was used heavily by IBM (which adopted it as the standard JavaScript framework for WebSphere products), the Australian Bureau of Statistics, the U.S. Department of Defense, and many large enterprise teams that needed the kind of comprehensive toolkit Dojo provided. It never had jQuery’s hobbyist or small-team adoption. By the mid-2010s, the framework wars had moved on and Dojo’s mindshare faded. The current Dojo 7 project is a complete rewrite around TypeScript and reactive patterns, maintained by a small team.

Prototype, Sam Stephenson, and the Rails Connection

Section titled “Prototype, Sam Stephenson, and the Rails Connection”

Prototype is the library that came bundled with one of the most influential web frameworks of the 2000s.

Sam Stephenson, a developer at 37signals (the company that produced Basecamp and originated Ruby on Rails), released Prototype.js in 2005. The library extended JavaScript with class-like inheritance, Ajax helpers, DOM utilities, and a small set of cross-browser conveniences. The original target was Rails — David Heinemeier Hansson included Prototype as part of Rails’ default JavaScript stack starting in version 1.0, which meant that every Rails application built between 2005 and 2010 had Prototype loaded by default.

This is the historical context Prototype is most clearly understood through. Rails was the most influential web framework of its era; Prototype was Rails’ JavaScript layer; for a substantial fraction of the working web developer population in the mid-to-late 2000s, frontend JavaScript meant Prototype.

Prototype’s design centered on extending the JavaScript language itself. Rather than wrap DOM elements in a collection object (jQuery’s approach), Prototype added methods directly to the built-in prototype chains:

// Prototype extended Array.prototype:
[1, 2, 3].each(function(n) { alert(n) })
// And added methods to elements via $:
$('mydiv').show().addClassName('selected')

The $('mydiv') returned an actual DOM element, not a wrapper — Prototype extended the element directly with additional methods. Inheritance was provided through Class.create:

var Animal = Class.create({
initialize: function(name) {
this.name = name
},
speak: function() {
return this.name + ' makes a sound'
}
})
var Dog = Class.create(Animal, {
speak: function($super) {
return $super() + ': bark!'
}
})

The Prototype style was attractive to developers who wanted class-based inheritance and wished JavaScript had it natively. It was less attractive to developers who had to debug what happened when Prototype’s Array.prototype extensions collided with other libraries’ code — a recurring problem that helped push the field toward jQuery’s wrapper-collection approach.

The reverence beat on Stephenson is worth landing. The Prototype design wasn’t a mistake. The choice to extend native prototypes — controversial in hindsight — was based on the reasonable design judgment of 2005, when JavaScript didn’t have classes and developers were searching for any way to make the language feel like a more conventional object-oriented language. The fact that the choice didn’t age well is partly about the choice itself and partly about the ecosystem dynamics it ran into. The Rails bundling decision gave Prototype its position; the Rails community’s eventual migration to jQuery (around Rails 3.1 in 2011) closed the same window.

Prototype had a companion library worth naming.

script.aculo.us (the name a deliberate joke about the dot-com domain trend) was written by Thomas Fuchs starting in 2005 as an animation and visual-effects library that sat on top of Prototype. Fuchs’s library provided fade transitions, sliding panels, draggable elements, sortable lists, and a small set of Effects (the Effect.Fade, Effect.Slide, Effect.Shrink family) that made interactive animation accessible at a time when CSS animations didn’t exist and manual setInterval loops were the alternative.

The script.aculo.us API was, by 2007, what most working developers reached for when they needed animation in a Prototype application. Rails templates routinely included it. Many of the small interactive flourishes characteristic of Web 2.0 design — yellow fade-in to confirm a save, slide-down for new content, gentle dim on remove — were either built with script.aculo.us or built in obvious imitation of its conventions.

Fuchs would later go on to write Zepto.js, a lightweight jQuery-compatible library targeting mobile devices, and other projects in the same lineage of small-but-useful frontend libraries.

MooTools — the My Object-Oriented Tools library — was written by Valerio Proietti starting in 2006 with a 1.0 release later that year.

Proietti’s design was a more rigorous extension of Prototype’s class-based approach. MooTools provided a full class system with mixins (called Implements), single inheritance (Extends), method overriding, and an opinionated style for organizing object-oriented JavaScript. The library also extended native prototypes the way Prototype did, but with more careful attention to which methods it added and how they interacted with other libraries.

MooTools was popular with developers who wanted classical inheritance and rigorous structure. It was less popular with developers who wanted ergonomic DOM manipulation, where jQuery was the easier choice. The library’s community was strong through the late 2000s — the MooTools More package added widgets and additional functionality, and the MooTools Forge package repository was an early attempt at an organized plugin ecosystem.

MooTools is worth knowing about for a small but specific reason. In 2011, when the ECMAScript committee was preparing what became ES2015, the proposed Array.prototype.contains method conflicted with a method MooTools had added to Array.prototype years earlier. Browsers that shipped the native contains method would silently break MooTools-based code. The committee renamed the method to Array.prototype.includes — the name we know it by today — specifically to avoid breaking the long tail of MooTools sites still in production. This is one of the field’s clearest examples of the the web does not break the web rule the standards bodies operate by, and MooTools is the reason includes is called includes.

YUI — the Yahoo! User Interface library — was released by Yahoo! in February 2006. Where Dojo was community-led and Prototype was Rails-led, YUI was firmly corporate. Yahoo! used the library internally for its own web properties and released it as an open-source package to the broader community.

YUI’s design was closer to Dojo’s than to jQuery’s. The library shipped a comprehensive set of components — a grid system, a calendar, a rich text editor, an autocomplete, a data table, charts, layouts. It included a module loader, a build tool, and a documentation generator. Doug Crockford, who had spent the previous several years arguing publicly that JavaScript was a serious language hidden inside a poorly-implemented ecosystem (Chapter 5 met him in the ES4 context), was Yahoo!‘s most visible JavaScript voice during this period and was associated with the YUI project’s culture.

YUI 2 was the version most working developers encountered. YUI 3, released in 2009, was a near-complete rewrite around a smaller core, async module loading, and a cleaner widget architecture. The library was well-engineered and widely used inside Yahoo!. Its broader adoption was limited by Yahoo!‘s declining brand through the 2010s. In 2014, Yahoo! announced that YUI would no longer receive new development. The library was archived. Yahoo!‘s internal teams migrated to other tools.

YUI’s lasting legacy is less the library itself than the documentation and educational material it produced. The YUI Theater series of recorded talks (with Crockford, Nate Koechley, Bill Scott, and many others) is still some of the most useful frontend training material from that era, and a substantial fraction of the field’s working developers in 2010–2014 had learned at least one core concept by watching one of those videos.

Classes Before Classes, Modules Before Modules

Section titled “Classes Before Classes, Modules Before Modules”

The deeper observation about this group of libraries is that they were each trying to fill the same set of gaps in the language and platform of their time.

JavaScript didn’t have classes until ES2015. Prototype, MooTools, Dojo, and YUI each invented a class system. They differed in syntax, in inheritance semantics, and in how they handled mixins, but each one was trying to give the developer a way to write object-oriented code in a language that didn’t natively support it. When ES2015 classes shipped, the field had a decade of opinions about what a JavaScript class should look like, drawn from the experience of these libraries.

JavaScript didn’t have modules until ES2015. Dojo, YUI, and (slightly later) RequireJS — by James Burke, the Dojo veteran — all built module systems with their own loaders, dependency declarations, and build tools. The two competing standards that emerged from this work — CommonJS (Node’s choice) and AMD (RequireJS’s choice) — defined how JavaScript code was organized for a decade. When ES2015 modules shipped, the field had two well-formed mental models for what a module should look like.

JavaScript didn’t have a widget toolkit. Dojo’s Dijit, YUI’s controls, MooTools More, and jQuery UI all built widget libraries. The patterns they used — a class per widget, a lifecycle of construction / startup / teardown, declarative HTML markup that the widget upgrades on initialization — became the patterns that custom elements would standardize twenty years later.

JavaScript didn’t have a standard build tool. Dojo’s build tool, YUI Compressor, and the later Grunt and Gulp generation each shipped their own. The Webpack and Vite era is the lineal descendant of this work.

The pattern is consistent enough to be a working principle of this book. The frontend ecosystem solves a platform gap in a library. The platform eventually absorbs the solution. The library becomes optional, but the patterns it established stay in the field’s vocabulary. Prototype’s Class.create is gone; ES classes are here. Dojo’s modules are gone; ES modules are here. The widget toolkits are gone; custom elements are here. The libraries did their work. The platform took it from them.

jQuery won, and Prototype/MooTools/Dojo/YUI didn’t, because jQuery was making a smaller and more practical argument.

Each of the libraries in this chapter was trying to solve application architecture. Each one had opinions about classes, modules, widgets, lifecycle, namespacing, and build tooling. Adopting any of them was, in practice, a commitment to a particular way of thinking about JavaScript applications. Teams either bought in to Dojo’s full toolkit or they didn’t. Prototype’s prototype extensions had to be compatible with the rest of the application, and conflicts were possible. MooTools required learning its class system.

jQuery asked for none of this. The library was a single file. The API was a function and a wrapped collection. You added a <script> tag and started using $(). There were no opinions about classes. There were no opinions about modules. There were no opinions about lifecycle. There was nothing to learn that wasn’t obvious from the first three minutes of using it.

The trade-off was that jQuery had no answer for application architecture, which the previous chapter described in detail. The lack of an answer was, in 2007, the right product decision. The market wasn’t asking for architecture. The market was asking for DOM access that worked across IE6 and Firefox without making the developer cry. jQuery solved that problem cleanly, and the architectural problem — which mattered more, eventually — was someone else’s to solve later.

That someone else turned out to be the next generation of frameworks. Backbone, Knockout, Angular, Ember, eventually React. The architectural ambitions of Dojo and YUI and MooTools didn’t disappear when those libraries faded; they migrated into the framework era, which is where the next chapter picks up the story.

A short accounting of what the libraries in this chapter contributed to the field’s working vocabulary, before we move on.

The widget pattern. A unit of interface with a defined lifecycle, declarative markup, JavaScript class, and configuration options. Modernized as custom elements and component frameworks.

The module pattern. Code organized into named, dependency-aware units with explicit imports and exports. Standardized as ES modules.

The class pattern. Object-oriented code organization with constructors, inheritance, and instance methods. Standardized as ES2015 classes.

The build-and-bundle pattern. A compile step that resolves modules, optimizes code, and produces a deployable artifact. Standardized as Webpack, then Vite, then esbuild and Bun.

The HTML-as-declarative-API pattern. A widget that can be instantiated from HTML markup as well as from JavaScript, with the framework upgrading the markup on initialization. Standardized as custom elements.

The plugin/extension pattern. A library that exposes a mechanism for third parties to extend its functionality, ideally with sensible namespacing and lifecycle. Modernized in npm packages, framework plugins, and custom-element ecosystems.

Each one of these patterns appears throughout the rest of this book, often without explicit reference to the library that originated it. The libraries in this chapter are largely forgotten as products. Their vocabulary is everywhere.

This chapter has been about the libraries that tried to bring application discipline to JavaScript before the language and platform had the primitives to support it. The next chapter is about what happened when the same architectural ambitions migrated into the framework generation — Backbone, Knockout, Angular, Ember — and started to seriously challenge the page-as-document model that all of the libraries in this chapter still assumed.

Exercise: Refactor Selector Code Into a Module

Section titled “Exercise: Refactor Selector Code Into a Module”

Start with a small selector-driven interaction:

document.querySelectorAll('.book-row').forEach(row => {
const button = row.querySelector('.delete-button')
button.addEventListener('click', () => {
row.remove()
})
})

Now grow the requirements. The interaction needs to confirm before deleting. It needs to emit a deletion event other code can observe. It needs to show a notification. It needs to log an audit entry. It needs to disable the delete button if the user lacks permission. And it needs to clean up its listeners if the row is removed for other reasons.

First, implement all of that directly in the click handler.

Then refactor it into a small module or class:

function createBookRowController(row, options) {
// initialize
// attach listeners
// expose destroy()
}

Reflection:

  1. What behavior needed a home?
  2. What state appeared in the process?
  3. What dependencies appeared — services, configuration, other modules?
  4. What lifecycle concerns appeared?
  5. Which concerns belonged to the row itself? Which belonged to larger application capabilities (auth, audit, notifications)?
  6. If you had to do this same refactor for ten different kinds of row, what pattern would you formalize?

This exercise should reveal why structure became necessary in JavaScript applications, and why the libraries in this chapter were trying to provide it — even if their specific solutions didn’t survive.