Advanced YUI Module Usage and Interactivity Patterns
From the 123 curriculum
Advanced YUI Module Usage and Interactivity Patterns
TL;DR
You'll learn how to structure your YUI applications using custom modules, manage dependencies effectively, and implement robust event-driven interactivity patterns. This lets you build scalable and maintainable JavaScript frontends with YUI.
1. The Mental Model
Think of your YUI application as a collection of specialized building blocks (modules) that communicate with each other. Each block handles a specific job, and they connect using a central messaging system (events). This keeps things organized and flexible.
2. The Core Material
When you're building larger YUI applications, you'll quickly outgrow just using Y.one() and basic events. Advanced module usage and interactivity patterns help you structure your code for maintainability and scalability.
Defining Custom YUI Modules

Photo by Matheus Bertelli on Pexels
YUI's module system lets you encapsulate your code, manage dependencies, and define public APIs. This is crucial for avoiding global variable pollution and organizing complex applications.
You define a module using YUI.add().
YUI.add('my-awesome-module', function(Y) {
// This is the scope of your module.
// 'Y' is the YUI instance for this module.
function _privateHelper() {
Y.log('This is a private helper function.', 'info', 'my-awesome-module');
}
// The 'exports' object holds what your module makes public.
Y.namespace('MyNamespace').AwesomeModule = {
init: function(config) {
Y.log('MyAwesomeModule initialized!', 'info', 'my-awesome-module');
// Do setup here, e.g., attach event listeners
_privateHelper();
if (config && config.message) {
Y.log('Config message: ' + config.message, 'info', 'my-awesome-module');
}
},
doSomething: function(data) {
Y.log('MyAwesomeModule is doing something with: ' + data, 'info', 'my-awesome-module');
}
};
}, '0.0.1', {
requires: ['node', 'event-base'] // Declare dependencies here
});
Here's how to use it:
YUI().use('my-awesome-module', function(Y) {
// Now you can access your module
Y.MyNamespace.AwesomeModule.init({ message: 'Hello from main app!' });
Y.MyNamespace.AwesomeModule.doSomething('some important data');
});
Event-Driven Interactivity with Custom Events

Photo by Marius Gabriel on Pexels
Instead of directly calling methods on other modules, a robust pattern is to use custom events. This decouples your modules, making them more flexible. One module can fire an event, and other modules can listen for it, without knowing about each other directly.
YUI.add('publisher-module', function(Y) {
// Define a custom event for this module.
// The `publish` method makes this event available.
Y.publish('dataAvailable', {
defaultFn: function(e) {
Y.log('Default handler for dataAvailable fired.', 'debug');
},
emitFacade: true, // Wrap event data in an Event Facade
fireOnce: false // Can fire multiple times
});
Y.namespace('Publisher').DataFetcher = {
fetchData: function(id) {
Y.log('Fetching data for ID: ' + id, 'info', 'publisher-module');
// Simulate async data fetching
setTimeout(function() {
const data = { id: id, value: Math.random() };
Y.log('Data fetched: ' + JSON.stringify(data), 'info', 'publisher-module');
// Fire the custom event with the data
Y.fire('dataAvailable', { data: data }); // Pass event payload
}, 500);
}
};
}, '0.0.1', { requires: ['event-custom'] });
YUI.add('subscriber-module', function(Y) {
Y.namespace('Subscriber').DataProcessor = {
init: function() {
Y.log('DataProcessor initialized, listening for data.', 'info', 'subscriber-module');
// Subscribe to the custom event defined in publisher-module
Y.on('dataAvailable', this.handleData, this); // 'this' refers to DataProcessor
},
handleData: function(e) {
Y.log('DataProcessor received data: ' + JSON.stringify(e.data), 'info', 'subscriber-module');
// Process the data...
}
};
}, '0.0.1', { requires: ['event-custom'] });
Here's how they interact:
YUI().use('publisher-module', 'subscriber-module', function(Y) {
Y.Subscriber.DataProcessor.init(); // Start listening
Y.Publisher.DataFetcher.fetchData(123); // Fire the event indirectly
Y.Publisher.DataFetcher.fetchData(456);
});
This sequence shows how modules interact without knowing each other's internals.
graph TD
A["YUI.add('publisher-module')"] --> B{Define Custom Event: "dataAvailable"};
B --> C["Publisher.DataFetcher.fetchData()"];
C --> D["Y.fire('dataAvailable', { data: ... })"];
E["YUI.add('subscriber-module')"] --> F["Subscriber.DataProcessor.init()"];
F --> G["Y.on('dataAvailable', handler, this)"];
D --> G;
G --> H["handler(e) receives event data"];
H --> I["Process data in Subscriber"];
style A fill:#cef,stroke:#333,stroke-width:2px;
style E fill:#cef,stroke:#333,stroke-width:2px;
style C fill:#ccf,stroke:#333,stroke-width:1px;
style G fill:#ccf,stroke:#333,stroke-width:1px;
Sandbox Pattern (Application-wide Events)

Photo by Karolina Grabowska www.kaboompics.com on Pexels
For truly decoupled communication across your entire application, you can leverage the YUI instance itself as a central event bus. Each module gets its own YUI instance (or uses the main one), and fires/listens for events on that instance. This is often called a "sandbox" or "event bus" pattern.
In the examples above, Y.fire('dataAvailable', ...) and Y.on('dataAvailable', ...) are already using the YUI instance as the event bus, making it an implicit sandbox. If you needed separate "sandboxes" for different parts of a very large application, you could create multiple YUI() instances, but for most apps, using the main Y instance works great.
3. Worked Example
Let's combine a UI component (a "clicker") with a "logger" module, using custom events to communicate.
HTML:
<div id="app">
<button id="myButton">Click Me!</button>
<ul id="logOutput"></ul>
</div>
JavaScript (combined for demonstration, but imagine these are separate YUI.add() calls):
```javascript
// Module 1: Clicker
YUI.add('clicker-module', function(Y) {
Y.publish('buttonClicked', { // Define the custom event
emitFacade: true,
fireOnce: false
});
Y.namespace('MyApp').Clicker = {
init: function(buttonSelector) {
this._button = Y.one(buttonSelector);
if (this._button) {
this._button.on('click', this._handleClick, this);
Y.log('Clicker module initialized and listening for clicks.', 'info', 'clicker-module');
} else {
Y.error('Button not found for selector: ' + buttonSelector, null, 'clicker-module');
}
},
_handleClick: function(e) {
Y.log('Button was clicked!', 'info', 'clicker-module');
Y.fire('buttonClicked', { // Fire the custom event
timestamp: new Date().toLocaleTimeString(),
clicks: (this._clicks || 0) + 1
});
this._clicks = (this._clicks || 0) + 1;
}
};
}, '0.0.1', { requires: ['node', 'event-custom'] });
// Module 2: Logger
YUI.add('logger-module', function(Y) {
Y.namespace('MyApp').Logger = {
init: function(outputSelector) {
this._output = Y.one(outputSelector);
if (this._output) {
// Subscribe to the custom event from the Clicker module
Y.on('buttonClicked', this._handleButtonClick, this);
Y.log('Logger module initialized and listening for button clicks.', 'info', 'logger-module');
} else {
Y.error('Log output element not found for selector: ' + outputSelector, null, 'logger-module');
}
},
_handleButtonClick: function(e) {
const message = Button clicked at ${e.timestamp}. Total clicks: ${e.clicks};
Y.log('Logger received: ' + message, 'info', 'logger-module');
if (this._output) {
const li = Y.Node.create(`
Frequently asked about Advanced YUI Module Usage and Interactivity Patterns
Study this next
Get the full 123 curriculum
Clone the complete plan to your dashboard for unlimited AI-generated notes, practice quizzes, and a personalised revision schedule.
Create Free Account