Created
January 23, 2012 16:07
-
-
Save bennadel/1663999 to your computer and use it in GitHub Desktop.
Creating A Keyboard-Shortcuts Module In A Modular JavaScript Application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Define the color swtch View controller. | |
define( | |
[ | |
"jquery", | |
"signal" | |
], | |
function( $, Signal ){ | |
// I return an initialized component. | |
function ColorSwatch( target ){ | |
// Create a collection of cached DOM elements that are | |
// required for this view to function. | |
this.dom = {}; | |
this.dom.target = target; | |
this.dom.action = this.dom.target.find( "a.action" ); | |
// Define the list of colors to be used to display the | |
// color swatch. | |
this.colors = [ | |
"#FF0066", | |
"#FF3366", | |
"#FF6666", | |
"#FF9966", | |
"#FFCC66", | |
"#FFFF66", | |
"#CCCCCC", | |
"#999999", | |
"#666666", | |
"#333333" | |
]; | |
// Store the current color index. | |
this.currentColorIndex = 0; | |
// Listen for the click event on the action. | |
this.dom.action.click( | |
$.proxy( this.handleClick, this ) | |
); | |
// Render the view based on the current internal state. | |
this.render(); | |
// Return this object reference. | |
return( this ); | |
} | |
// Define the class methods. | |
ColorSwatch.prototype = { | |
// I return the currently selected color. | |
getColor: function(){ | |
// Return the color at the current index. | |
return( this.colors[ this.currentColorIndex ] ); | |
}, | |
// I handle the click of the action. | |
handleClick: function( event ){ | |
// Cancel the default behavior since this isn't a | |
// real link. | |
event.preventDefault(); | |
// Go to the next color. | |
this.nextColor(); | |
}, | |
// I move to the next color. | |
nextColor: function(){ | |
// Increment the color index. | |
this.currentColorIndex++; | |
// If we moved to far, then loop back around. | |
if (this.currentColorIndex >= this.colors.length){ | |
// Go back to the beginning of the color list. | |
this.currentColorIndex = 0; | |
} | |
// Re-render the view with the new color. | |
this.render(); | |
}, | |
// I update the display of the swatch based on the | |
// currently selected color; | |
render: function(){ | |
// Update the swatch color. | |
this.dom.target.css( | |
"background-color", | |
this.getColor() | |
); | |
} | |
}; | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// Return the constructor for this view. | |
return( ColorSwatch ); | |
} | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Define the Controller for the demo. | |
define( | |
[ | |
"jquery", | |
"signal", | |
"view/toggle-keys", | |
"view/color-swatch", | |
"view/keyboard-shortcuts" | |
], | |
function( $, Signal, ToggleKeys, ColorSwatch, KeyboardShortcuts ){ | |
// I return an initialized component. | |
function Demo(){ | |
// Create our toggle keys module. | |
this.toggleKeys = new ToggleKeys( | |
$( "div.toggle" ) | |
); | |
// Create our color swatch module. | |
this.colorSwatch = new ColorSwatch( | |
$( "div.swatch" ) | |
); | |
// Create our keyboard shortcut module. | |
this.keyboardShortcuts = new KeyboardShortcuts( | |
$( document ) | |
); | |
// Bind to the toggle module to listen for toggle events. | |
this.toggleKeys.events.toggled.bind( | |
this.handleToggle, | |
this | |
); | |
// Bind to the keyboard shortcuts event. | |
this.keyboardShortcuts.events.nextColor.bind( | |
this.handleNextColor, | |
this | |
); | |
// Check to see if the keyboard short-cuts are currently | |
// on or off. If they are on, we need to start watching | |
// the keys. | |
if (this.toggleKeys.isOn()){ | |
// Start watching keyboard events. | |
this.keyboardShortcuts.start(); | |
} | |
// Return this object reference. | |
return( this ); | |
} | |
// Define the class methods. | |
Demo.prototype = { | |
// I handle the nextColor keyboard shortcut. | |
handleNextColor: function( event ){ | |
// Pass this request onto the appropriate module. | |
this.colorSwatch.nextColor(); | |
}, | |
// I handle a change on the toggle. | |
handleToggle: function( event, status ){ | |
// If the toggle is On, start watching keys. | |
if (event.context.isOn()){ | |
// Start watching keys. | |
this.keyboardShortcuts.start(); | |
} else { | |
// Stop watching keys. | |
this.keyboardShortcuts.stop(); | |
} | |
} | |
}; | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// Return the constructor for this controller. | |
return( Demo ); | |
} | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Define the keyboard shortcuts view controller. | |
define( | |
[ | |
"jquery", | |
"signal" | |
], | |
function( $, Signal ){ | |
// I return an initialized component. | |
function KeyboardShortcuts( stage ){ | |
// Create a collection of cached DOM elements that are | |
// required for this view to function. | |
this.dom = {}; | |
// Since this view isn't really associated with any | |
// particular DOM element, it will listen to events on | |
// the stage (root element). | |
this.dom.stage = stage; | |
// For optimization purposes, we'll cache an "active" | |
// input field so that we only have to listen to keyboard | |
// events that are necessary. | |
this.dom.activeInput = null; | |
// Create an event factory. | |
var signalFactory = Signal.forContext( this ); | |
// Create an event landscape for subscriber binding. | |
// In this case, we will define an event for each | |
// keyboard shortcut that can be triggered. For this | |
// demo, there is only one. | |
this.events = {}; | |
this.events.nextColor = signalFactory( "nextColor" ); | |
// Return this object reference. | |
return( this ); | |
} | |
// Define the class methods. | |
KeyboardShortcuts.prototype = { | |
// I handle the blur of the currently active input. | |
handleBlur: function(){ | |
// Unbind our blur handler - the input is no longer | |
// active. | |
this.dom.activeInput.off( | |
"blur.keyboardShortcuts", | |
this.handleBlur | |
); | |
// Remove the active reference for our input. | |
this.dom.activeInput = null; | |
// Start watching for global keyboard events again. | |
this.watchKeys(); | |
}, | |
// I handle the global key events. | |
handleKeyPress: function( event ){ | |
// Get the active element at the time the event was | |
// triggered. | |
var target = $( event.target ); | |
// Check to see if we are in an input field. If so, | |
// we want to allow the user to continue using the | |
// UI as usual. | |
if (target.is( ":input" )){ | |
// It is likely that many keyboard events will be | |
// triggered while the Input is focused. As such, | |
// let's stop listening to keyboard events until | |
// the user has left the input (blur). | |
this.stopWatchingKeys(); | |
// Cache the active input (in case). | |
this.dom.activeInput = target; | |
// Now, wait for the blur on the Input to re-bind | |
// the global key handler. | |
target.on( | |
"blur.keyboardShortcuts", | |
$.proxy( this.handleBlur, this ) | |
); | |
// Return out - we don't want to process this | |
// event any further. | |
return; | |
// Make sure that there are not modifier keys being | |
// pressed - we don't want to start overriding the | |
// native features of the browser. | |
} else if ( | |
event.ctrlKey || | |
event.altKey || | |
event.metaKey | |
){ | |
// The user is typing in an input - we don't want | |
// to intercept that kind of action. | |
return; | |
} | |
// If we've made it this far then the key being | |
// pressed is a valid candidate for our keybaord | |
// shortcuts. Now, get the key character. | |
var keyChar = String.fromCharCode( event.which ) | |
.toLowerCase() | |
; | |
// For DEBUGGING ONLY ------ // | |
console.log( "Looking At: ", keyChar ); | |
// For DEBUGGING ONLY ------ // | |
// Check to see if this key character matches a | |
// keyboard shortcut within our system. | |
if (keyChar === "c"){ | |
// This is a known keyboard shortcut. Prevent the | |
// default behavior so we can override the native | |
// function of the browser. | |
event.preventDefault(); | |
// Trigger the event for going to the next Color. | |
this.events.nextColor.trigger(); | |
} | |
}, | |
// I activate the module. | |
start: function(){ | |
// Start watching the keyboard shortcuts. | |
this.watchKeys(); | |
}, | |
// I deactivate the module until explicitly told to start | |
// watching the keys again. | |
stop: function(){ | |
// Stop watching the keys. | |
this.stopWatchingKeys(); | |
// Check to see if we are watching any input for | |
// further activity. | |
if (this.dom.activeInput){ | |
// Unbind the blur handler - this is no longer | |
// a relevant event in our workflow. | |
this.dom.activeInput.off( | |
"blur.keyboardShortcuts", | |
this.handleBlur | |
); | |
// Clear the active input. | |
this.dom.activeInput = null; | |
} | |
}, | |
// I stop watching the keyboard inputs at the global | |
// level. | |
stopWatchingKeys: function(){ | |
// Unbind our global key handler. | |
this.dom.stage.off( | |
"keypress.keyboardShortcuts", | |
this.handleKeyPress | |
); | |
}, | |
// I start watching keyboard inputs at the global level. | |
watchKeys: function(){ | |
// Start watching keyboard events that bubble up to | |
// the stage. | |
this.dom.stage.on( | |
"keypress.keyboardShortcuts", | |
$.proxy( this.handleKeyPress, this ) | |
); | |
} | |
}; | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// Return the constructor for this view. | |
return( KeyboardShortcuts ); | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment