Created
January 20, 2012 21:46
-
-
Save bennadel/1649769 to your computer and use it in GitHub Desktop.
Handling Keyboard Shortcuts Within Modular JavaScript Application Architecture
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( sandbox, target ){ | |
// Store the sandbox - this is the object through which | |
// we will be able to communicate with the world outside | |
// of our module. | |
this.sandbox = sandbox; | |
// 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", | |
]; | |
// Store the current color index. | |
this.currentColorIndex = 0; | |
// Listen for the click event on the action. | |
this.dom.action.click( | |
$.proxy( this.handleClick, this ) | |
); | |
// Listen for the key event on the sandbox - the user can | |
// use "c" to toggle through the colors without clicking | |
// on the action item. | |
this.sandbox.events.keyPressed.bind( | |
this.handleKeyPress, | |
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 handle the pressing of a key. | |
handleKeyPress: function( event, keyCode, keyChar ){ | |
// Check to see if the key is "c" (account of upper- | |
// case "C" as well. | |
if ( | |
(keyChar === "c") || | |
(keyChar === "C") | |
){ | |
// 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" | |
], | |
function( $, Signal, ToggleKeys, ColorSwatch ){ | |
// I return an initialized component. | |
function Demo(){ | |
// Before we create our color swatch module, we have | |
// to define the sandbox that it will use to listen | |
// for key presses. | |
this.sandbox = {}; | |
this.sandbox.events = {}; | |
this.sandbox.events.keyPressed = new Signal( this, "keyPressed" ); | |
// Create our toggle keys module. | |
this.toggleKeys = new ToggleKeys( $( "div.toggle" ) ); | |
// Create our color swatch - this one requires the | |
// sandbox for keyboard short-cuts. | |
this.colorSwatch = new ColorSwatch( | |
this.sandbox, | |
$( "div.swatch" ) | |
); | |
// Bind to the toggle module to listen for toggle events. | |
this.toggleKeys.events.toggled.bind( | |
this.handleToggle, | |
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.watchKeys(); | |
} | |
// Return this object reference. | |
return( this ); | |
} | |
// Define the class methods. | |
Demo.prototype = { | |
// I handle the key press to see how the key should be | |
// routed - not all key presses can be considered | |
// keyboard short-cuts. | |
handleKeyPress: function( event ){ | |
// Before we can route the key, check to make sure | |
// that the key is not in an input and that there | |
// are no "functional" modification keys being | |
// pressed -- those might indicate the use of a | |
// feature that is not meta to the browser view. | |
if ( | |
$( event.target ).is( ":input" ) || | |
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 made it this far, this is a key strong that | |
// we can intercept. Cancel to default event since we | |
// want to route this. | |
event.preventDefault(); | |
// Get the key code. | |
var keyCode = event.which; | |
// Get the key character. | |
var keyChar = String.fromCharCode( keyCode ); | |
// Trigger the key on the sandbox. | |
this.sandbox.events.keyPressed.trigger( | |
keyCode, | |
keyChar | |
); | |
}, | |
// 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.watchKeys(); | |
} else { | |
// Stop watching keys. | |
this.unwatchKeys(); | |
} | |
}, | |
// I stop watching the keyboard. | |
unwatchKeys: function(){ | |
// Unbind the key listener. | |
$( document ).off( "keypress.demo" ); | |
}, | |
// I start watching the keys pressed by the user. | |
watchKeys: function(){ | |
// Start listening for global key strokes. | |
$( document ).on( | |
"keypress.demo", | |
$.proxy( this.handleKeyPress, this ) | |
); | |
} | |
}; | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Toggling Quick-Keys For Modular JavaScript Components</title> | |
<!-- Link styles. ---> | |
<link rel="stylesheet" type="text/css" href="./assets/styles.css"></link> | |
<!-- Load the script loader and boot-strapping code. --> | |
<script | |
type="text/javascript" | |
src="./js/lib/require/require.js" | |
data-main="./js/main"> | |
</script> | |
</head> | |
<body> | |
<h1> | |
Toggling Quick-Keys For Modular JavaScript Components | |
</h1> | |
<!-- BEGIN: Toggle Module. --> | |
<div class="toggle"> | |
<a href="#" class="action">Toggle Quick-Keys:</a> | |
<span class="status">Off</span> | |
</div> | |
<!-- END: Toggle Module. --> | |
<!-- BEGIN: Color Swatch Module. --> | |
<div class="swatch"> | |
<a href="#" class="action"><br /></a> | |
</div> | |
<!-- END: Color Swatch Module. --> | |
<!-- | |
This element is just a decoy to make sure we don't listen | |
to keyboard keys unnecessarily. | |
--> | |
<form> | |
<p> | |
This is a decoy!<br /> | |
<input type="text" size="30" /> | |
</p> | |
</form> | |
</body> | |
</html> |
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 quick-key Toggle View controller. | |
define( | |
[ | |
"jquery", | |
"signal" | |
], | |
function( $, Signal ){ | |
// I return an initialized component. | |
function ToggleKeys( target ){ | |
// Create a collection of cached DOM elements that are | |
// required for this view to function. | |
this.dom = {}; | |
this.dom.target = target; | |
this.dom.toggle = this.dom.target.find( "a.action" ); | |
this.dom.status = this.dom.target.find( "span.status" ); | |
// Store the current status. | |
this.status = ToggleKeys.STATUS_OFF; | |
// Create an event factory. | |
var signalFactory = Signal.forContext( this ); | |
// Create an event landscape for subscriber binding. | |
this.events = {}; | |
this.events.toggled = signalFactory( "toggled" ); | |
this.events.toggledOn = signalFactory( "toggledOn" ); | |
this.events.toggledOff = signalFactory( "toggledOff" ); | |
// Listen for the click event on the toggle. | |
this.dom.toggle.click( | |
$.proxy( this.handleClick, this ) | |
); | |
// Render the view based on the current internal state. | |
this.render(); | |
// Return this object reference. | |
return( this ); | |
} | |
// Create some constants. | |
ToggleKeys.STATUS_ON = 1; | |
ToggleKeys.STATUS_OFF =2; | |
// Define the class methods. | |
ToggleKeys.prototype = { | |
// Handle the click of the action. | |
handleClick: function( event ){ | |
// Cancel the default behavior since this isn't a | |
// real link. | |
event.preventDefault(); | |
// Toggle the status. | |
this.toggle(); | |
}, | |
// I determine if the toggle is off. | |
isOff: function(){ | |
// Return true if off. | |
return( this.status === ToggleKeys.STATUS_OFF ); | |
}, | |
// I determine if the toggle is on. | |
isOn: function(){ | |
// Return true if on. | |
return( this.status === ToggleKeys.STATUS_ON ); | |
}, | |
// I update the display of the toggle based on the | |
// current status. | |
render: function(){ | |
// Update the text of the status to reflect the | |
// current state. | |
this.dom.status.text( | |
this.isOn() | |
? "On" | |
: "Off" | |
); | |
}, | |
// I toggle the current status. | |
toggle: function(){ | |
// Check the current status to see how we need | |
// to change it. | |
this.status = ( | |
this.isOn() | |
? ToggleKeys.STATUS_OFF | |
: ToggleKeys.STATUS_ON | |
); | |
// Render the view for the new status. | |
this.render(); | |
// Check to see which sub-toggle-event we should | |
// trigger based on the status. | |
if (this.isOn()){ | |
// Toggled on. | |
this.events.toggledOn.trigger(); | |
} else { | |
// Toggled off. | |
this.events.toggledOff.trigger(); | |
} | |
// Trigger the toggled event. | |
this.events.toggled.trigger( this.status ); | |
} | |
}; | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// Return the constructor for this view. | |
return( ToggleKeys ); | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment