PHASE 0

JavaScript & HTML Foundations for Dojo 1.17.3

Prerequisites every senior developer must be solid on before the Dojo learning path begins

JavaScript Closures Prototypes ES5 Dojo Setup DevTools
Who this phase is for: Even if you've been writing JavaScript for years, Dojo uses several JS patterns in ways you may not have seen in modern ES6+ code. This phase maps those patterns so nothing in later phases surprises you.
1

Closures & the this Binding

A closure is a function that remembers the variables from its outer scope even after that outer function has returned. Dojo uses closures extensively inside AMD module factories — every module you write is a closure.

// Classic closure — the counter pattern
function makeCounter() {
  var count = 0;          // outer variable

  return {
    increment: function() { count++; },   // closes over 'count'
    value:     function() { return count; }
  };
}

var c = makeCounter();
c.increment();
c.increment();
console.log(c.value());   // 2
// 'count' is not accessible from outside — it's private

Dojo widget callbacks use closures to capture the widget instance:

// Inside a Dojo widget's postCreate
postCreate: function() {
  var self = this;   // capture 'this' for use inside callbacks

  on(this.domNode, "click", function(e) {
    // 'this' here is the DOM event target — NOT the widget
    // 'self' is the widget — captured via closure
    self.set("selected", true);
  });
}
⚠ GOTCHA
In Dojo event callbacks, this is not the widget. Always capture it as var self = this in postCreate, or use lang.hitch(this, handler) from dojo/_base/lang.

The this Binding Rules

JavaScript determines this at call time, not at definition time. Four rules apply, in priority order:

RuleHow this is setDojo Example
new binding The new object being created new MyWidget()
Explicit binding .call(obj) / .apply(obj) / .bind(obj) lang.hitch(widget, fn)
Implicit binding The object to the left of the dot widget.destroy()this = widget
Default binding window (or undefined in strict mode) Bare callback: setTimeout(widget.render, 100)
require(["dojo/_base/lang"], function(lang) {

  var widget = {
    label: "Submit",
    onClick: function() {
      console.log(this.label);   // depends on how this is called
    }
  };

  // WRONG — 'this' will be window/undefined
  var bad = widget.onClick;
  bad();   // → undefined

  // CORRECT — hitch binds 'this' permanently to widget
  var good = lang.hitch(widget, widget.onClick);
  good();  // → "Submit"
});
2

Prototypal Inheritance

Dojo's declare() system is built directly on top of JavaScript's prototype chain. Understanding prototypes means you can debug inheritance issues, know when this.inherited() is needed, and understand why widget properties shared across instances can cause bugs.

// JavaScript prototype chain — raw form
function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  return this.name + " makes a noise.";
};

function Dog(name) {
  Animal.call(this, name);   // call parent constructor
}
// Set up prototype chain
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
  return this.name + " barks.";
};

var d = new Dog("Rex");
console.log(d.speak());           // "Rex barks."
console.log(d instanceof Dog);    // true
console.log(d instanceof Animal); // true

Dojo's declare automates exactly this pattern. Understanding the raw form helps you reason about what declare generates under the hood.

The Prototype Trap with Object/Array Properties

⚠ CRITICAL DOJO GOTCHA
Object and array properties defined on a declare class body are placed on the prototype, meaning they are shared across all instances. This is a very common source of bugs.
var MyWidget = declare(null, {
  // BUG: 'items' is on the prototype — shared by ALL instances
  items: [],

  addItem: function(item) {
    this.items.push(item);  // mutates the shared prototype array!
  }
});

var w1 = new MyWidget();
var w2 = new MyWidget();
w1.addItem("apple");
console.log(w2.items);  // ["apple"] �? BUG! w2 sees w1's item

// FIX: initialize objects/arrays in constructor
var MyWidgetFixed = declare(null, {
  constructor: function() {
    this.items = [];   // each instance gets its own array
  },
  addItem: function(item) {
    this.items.push(item);
  }
});
3

IIFE Pattern — Why AMD Uses It

An Immediately Invoked Function Expression (IIFE) creates a private scope to avoid polluting the global namespace. Before AMD, this was the standard way to encapsulate JavaScript modules.

// IIFE pattern — creates isolated scope
(function() {
  var privateVar = "I am private";

  window.myModule = {
    greet: function() { return privateVar; }
  };
})();   // �? immediately invoked

console.log(privateVar);         // ReferenceError — not accessible
console.log(myModule.greet());   // "I am private"

AMD's define() wraps your module factory in exactly this pattern — but the loader manages the scope for you:

// AMD define — the factory IS an IIFE, managed by the loader
define(["dojo/_base/declare"], function(declare) {
  // Everything here is in a private scope
  var privateHelper = function(x) { return x * 2; };

  // Only what you return is exported
  return declare(null, {
    double: function(x) { return privateHelper(x); }
  });
});
// privateHelper is NOT accessible outside this module

This is why you never see var dojo = ... littering the global scope in properly structured Dojo apps.

4

The arguments Object & Function.prototype.apply

Dojo's this.inherited(arguments) — the way you call a parent class method — relies entirely on the arguments object. Understanding it is non-negotiable.

function sum() {
  // 'arguments' is an array-like object — NOT a real Array
  var total = 0;
  for (var i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}
console.log(sum(1, 2, 3));   // 6

// Converting arguments to a real Array (ES5 style)
function toArray() {
  return Array.prototype.slice.call(arguments);
}
console.log(toArray(1, 2, 3));  // [1, 2, 3]

Why this.inherited(arguments) Works

When you call this.inherited(arguments) inside a Dojo widget method, you are passing the live arguments object of the current function up to the parent's version of that same method. Dojo uses arguments.callee (in non-strict mode) or its own tracking to identify which method is being called.

var Animal = declare(null, {
  speak: function(volume) {
    return "noise at " + volume;
  }
});

var Dog = declare(Animal, {
  speak: function(volume) {
    var parentResult = this.inherited(arguments);
    // arguments here is [volume] — passed intact to Animal.speak
    return parentResult + " (barking)";
  }
});

var d = new Dog();
console.log(d.speak("loud"));   // "noise at loud (barking)"
Rule: Always call this.inherited(arguments) — never this.inherited([arg1, arg2]). The magic is in passing the live arguments object, not a new array.
5

ES5 Array Methods

Dojo's store query results and NodeList collections use the same functional iteration model as ES5 arrays. Knowing these saves you from writing manual for loops everywhere.

var employees = [
  { id: 1, name: "Alice", dept: "Engineering", salary: 95000 },
  { id: 2, name: "Bob",   dept: "Marketing",   salary: 72000 },
  { id: 3, name: "Carol", dept: "Engineering", salary: 105000 },
  { id: 4, name: "Dave",  dept: "Marketing",   salary: 68000 }
];

// forEach — iterate with side effects
employees.forEach(function(emp) {
  console.log(emp.name);
});

// filter — returns new array matching predicate
var engineers = employees.filter(function(emp) {
  return emp.dept === "Engineering";
});
// [ Alice, Carol ]

// map — transform each element
var names = employees.map(function(emp) { return emp.name; });
// ["Alice", "Bob", "Carol", "Dave"]

// reduce — accumulate a value
var totalSalary = employees.reduce(function(acc, emp) {
  return acc + emp.salary;
}, 0);
// 340000

// some / every
var anyHighEarner = employees.some(function(e) { return e.salary > 100000; });
// true

// sort (mutates — always work on a copy)
var sorted = employees.slice().sort(function(a, b) {
  return a.salary - b.salary;   // ascending
});

In Dojo store queries, these same operations appear as store methods:

require(["dojo/store/Memory"], function(Memory) {
  var store = new Memory({ data: employees });

  // dojo/store filter = array filter
  var engResults = store.query({ dept: "Engineering" });

  // forEach on results
  engResults.forEach(function(emp) {
    console.log(emp.name);
  });
});
6

Callback Patterns vs Early Promise Concepts

Dojo 1.17 uses its own Deferred class (Phase 5 deep-dive) which predates native Promises. Understanding callback patterns first makes Deferred's design obvious.

// Callback hell — the problem Deferred solves
fetchUser(1, function(user) {
  fetchOrders(user.id, function(orders) {
    fetchProduct(orders[0].productId, function(product) {
      fetchReview(product.id, function(review) {
        // 4 levels deep — hard to read, hard to handle errors
        render(user, orders, product, review);
      }, onError);
    }, onError);
  }, onError);
}, onError);

// The same flow with Dojo Deferred (covered in Phase 5)
fetchUser(1)
  .then(function(user)    { return fetchOrders(user.id); })
  .then(function(orders)  { return fetchProduct(orders[0].productId); })
  .then(function(product) { return fetchReview(product.id); })
  .then(function(review)  { render(review); })
  .otherwise(onError);   // single error handler for all steps

The Node-Style Callback Convention

// Error-first callbacks — pattern used in many Dojo-adjacent libs
function loadData(url, callback) {
  // callback signature: function(error, result)
  xhr(url, function(err, data) {
    if (err) {
      callback(err, null);   // error in first position
    } else {
      callback(null, data);  // null error means success
    }
  });
}

loadData("/api/users", function(err, users) {
  if (err) { showError(err); return; }
  render(users);
});
7

HTML Boilerplate for Dojo

Dojo has several specific requirements for HTML setup that differ from a typical Bootstrap or React app. Getting these wrong causes silent failures that are frustrating to debug.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Dojo App</title>

  <!-- 1. Dijit theme CSS MUST come before dojo.js -->
  <link rel="stylesheet" href="dojo/resources/dojo.css">
  <link rel="stylesheet" href="dijit/themes/claro/claro.css">

  <!-- 2. dojoConfig MUST be set BEFORE dojo.js loads -->
  <script>
    var dojoConfig = {
      async: true,          // use AMD loader (always true for 1.7+)
      parseOnLoad: false,   // ALWAYS false — parse manually
      packages: [
        { name: "myapp", location: "/js/myapp" }
      ]
    };
  </script>

  <!-- 3. dojo.js loaded synchronously in <head> -->
  <script src="dojo/dojo.js"></script>
</head>

<!-- 4. Theme class MUST be on <body> — not <html>, not a wrapper div -->
<body class="claro">

  <div id="app"></div>

  <!-- 5. App bootstrap at end of body -->
  <script>
    require(["myapp/App", "dojo/domReady!"], function(App) {
      new App().placeAt("app").startup();
    });
  </script>
</body>
</html>
RequirementWhy it matters
async: true in dojoConfig Enables the AMD loader. Without it, you get legacy synchronous loading — much slower.
parseOnLoad: false Prevents the parser auto-scanning the entire DOM. Always call parser.parse() explicitly.
dojoConfig before dojo.js dojo.js reads dojoConfig at load time. If it's defined after, settings are ignored silently.
Theme class on <body> Dijit CSS selectors start with .claro — must be an ancestor of all widgets.
Theme CSS before dojo.js Prevents FOUC (flash of unstyled content) when widgets render.

Dijit Themes — What's Available

ThemeLook & FeelBest For
claroClean light blue/grayModern professional apps
tundraMuted gray tonesLegacy corporate apps
soriaBlue gradient header barsDashboard-style apps
nihiloMinimal whiteClean content-focused apps
8

CSS Concepts for Dijit

Dijit widgets have very specific CSS requirements. Three concepts cause the most friction for developers new to Dijit.

1. Box Model

Dijit uses box-sizing: content-box by default (not border-box like Bootstrap). This means width does not include padding or border. Layout widgets calculate sizes precisely — mixing border-box in your own CSS while wrapping Dijit widgets breaks layout math.

/* SAFE — set border-box only on your own elements */
.my-wrapper { box-sizing: border-box; }

/* DANGEROUS — this breaks Dijit layout calculations */
*, *::before, *::after { box-sizing: border-box; }

2. CSS Specificity — Theming Dijit Safely

Dijit theme CSS selectors are intentionally low specificity (e.g., .claro .dijitButton). Override them by prefixing with your container class:

/* Low-risk override — scoped to your container */
.myapp-toolbar .dijitButton {
  border-radius: 4px;
  font-weight: 600;
}

/* HIGH RISK — overrides ALL dijitButton everywhere */
.dijitButton {
  border-radius: 4px;
}

3. Positioning — How BorderContainer Works

Dijit BorderContainer sets position: absolute on all its child regions. Your container must have an explicit height — height: 100% or height: 100vh. Without it, regions collapse to zero height.

/* Required CSS for a full-page BorderContainer layout */
html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;   /* prevent double scrollbars */
}
#appLayout {
  height: 100%;       /* BorderContainer needs explicit height */
  width: 100%;
}

4. The dijitDisplayNone Class

Never use display: none to hide a Dijit widget's container — this causes sizing bugs when the widget is later shown. Use the widget's own API:

// WRONG — hides the DOM node but widget internals stay broken
myWidget.domNode.style.display = "none";

// CORRECT — widget handles visibility properly
myWidget.set("style", "display: none");  // or
require(["dojo/dom-style"], function(domStyle) {
  domStyle.set(myWidget.domNode, "display", "none");
});
9

Browser DevTools for Dojo Debugging

Dojo registers itself on the global require and dijit objects. These are your primary debugging tools from the browser console.

Widget Registry Inspection

// List ALL widgets currently alive on the page
dijit.registry.forEach(function(widget) {
  console.log(widget.id, widget.declaredClass, widget.domNode);
});

// Find a widget by its DOM node (e.g., from an Elements panel click)
var el = document.querySelector(".myDiv");
var widget = dijit.getEnclosingWidget(el);
console.log(widget);

// Get a widget directly by id
var myDialog = dijit.byId("confirmDialog");
myDialog.show();   // call methods directly from console

// Check if a DOM node has a widget attached
dijit.byNode(document.getElementById("myNode"));

AMD Module Inspection

// See which AMD modules have been loaded
Object.keys(require.modules || {}).sort().forEach(function(m) {
  console.log(m);
});

// Check if a specific module is loaded (no network request)
require(["dojo/domReady!"], function() {
  var mod = require("dijit/form/Select");   // returns cached module
  console.log(mod);
});

Debugging Layout Issues

// Check computed sizes of a BorderContainer's regions
var bc = dijit.byId("mainLayout");
bc.getChildren().forEach(function(child) {
  console.log(
    child.region,
    child.domNode.offsetWidth + "x" + child.domNode.offsetHeight
  );
});

// Force a layout recalculation (after dynamic resize)
bc.layout();

// Check if startup() was called (a very common oversight)
var tab = dijit.byId("myTab");
console.log(tab._started);   // true if startup() was called

Event Handle Leak Detection

// Track handles on a widget to catch leaks
var MyWidget = declare([_Widget], {
  postCreate: function() {
    this._handles = [];   // always store handles

    this._handles.push(
      on(this.domNode, "click", lang.hitch(this, "_onClick"))
    );
  },
  destroy: function() {
    // Clean up ALL handles before destroying
    this._handles.forEach(function(h) { h.remove(); });
    this.inherited(arguments);
  }
});
Chrome DevTools tip: Install the Dojo Developer Tools Chrome extension for a dedicated Dojo panel showing the widget tree, registry, and pub/sub messages in real time.

🔧 Hands-On Exercise 0.10 — Debug a Broken Dojo Page

Save the following HTML as debug-exercise.html and open it in a browser. It has 5 intentional bugs. Use what you learned in this phase to find and fix all 5.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="dijit/themes/claro/claro.css">
  <script src="dojo/dojo.js"></script>
  <script>
    var dojoConfig = { async: true, parseOnLoad: true };
  </script>
</head>
<body>
  <div id="container" style="height:300px"></div>

  <script>
    require(["dijit/layout/BorderContainer",
             "dijit/layout/ContentPane",
             "dojo/domReady!"],
    function(BorderContainer, ContentPane) {

      var layout = new BorderContainer({
        design: "headline"
      }, "container");

      var header = new ContentPane({
        region: "top",
        content: "Header"
      });

      var center = new ContentPane({
        region: "center",
        content: "Main content"
      });

      layout.addChild(header);
      layout.addChild(center);

      layout.placeAt("container");

      // No startup call

      var items = [];   // widget property defined here

      var MyCard = declare(null, {
        items: [],
        addItem: function(x) { this.items.push(x); }
      });

      var card1 = new MyCard();
      var card2 = new MyCard();
      card1.addItem("test");
      console.log("card2.items:", card2.items);  // expected: []
    });
  </script>
</body>
</html>
Bug 1: dojoConfig is defined after dojo.js loads — move it before the script tag.

Bug 2: parseOnLoad: true — should always be false. Causes double parsing and performance issues.

Bug 3: Missing class="claro" on <body> — Dijit widgets will render unstyled.

Bug 4: startup() is never called on layout — the BorderContainer renders but regions do not lay out correctly. Add layout.startup(); after placeAt.

Bug 5: items: [] on the MyCard prototype is shared between all instances. card2.items shows ["test"] because both cards share the same array. Fix: initialize in constructor: function() { this.items = []; }.

Phase 0 — Quick Reference

ConceptDojo Pattern That Uses It
ClosuresAMD module factory, widget callback handlers
this bindinglang.hitch(), widget lifecycle methods
Prototype chaindeclare() inheritance, mixin chain
Prototype trapAlways init objects/arrays in constructor()
IIFEAMD define() module factory
arguments objectthis.inherited(arguments)
ES5 array methodsStore query results, NodeList operations
CallbacksFoundation for understanding Deferred
HTML boilerplatedojoConfig before script, theme class on body
CSS specificitySafe Dijit theme overrides
DevToolsdijit.registry, getEnclosingWidget

Ready for Phase 1?

With these foundations solid, you're ready to learn how Dojo's AMD module system works and how every piece of Dojo is loaded on demand.

Continue to Phase 1: AMD Module System →