Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created January 20, 2012 21:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/1649769 to your computer and use it in GitHub Desktop.
Save bennadel/1649769 to your computer and use it in GitHub Desktop.
Handling Keyboard Shortcuts Within Modular JavaScript Application Architecture
// 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 );
}
);
// 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 );
}
);
<!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>
// 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