Touch, mouse, and pen/stylus interactions are received, processed, and managed as pointer input in Windows Store apps using JavaScript.
Roadmap: How does this topic relate to others? See Roadmap for Windows Store apps using JavaScript.
Note The framework control libraries provide the full Windows 8 user interaction experience, including standard interactions, animated physics effects, and visual feedback. If you don't need customized touch support, use these built-in controls. If the framework controls are not sufficient, these user interaction guidelines will help you to provide a compelling and immersive interaction experience that is consistent across input modes:
- Guidelines for user interaction
- Guidelines for cross-slide
- Guidelines for optical zoom and resizing
- Guidelines for panning
- Guidelines for rotation
- Guidelines for semantic zoom
- Guidelines for selecting text and images
- Guidelines for targeting
- Guidelines for visual feedback
Objective: To learn how to listen for and handle pointer input.
Prerequisites
We assume that you can create a basic Windows Store app using JavaScript that uses the Windows Library for JavaScript template.
To complete this tutorial, you need:
- Windows 8 and Microsoft Visual Studio Express 2012 for Windows 8. To download them, see Get the tools.
- A developer license. For instructions, see Get a developer license.
- For instructions on creating your first Windows Store app, see Getting started with Windows Store apps using JavaScript.
- For info on using Windows Library for JavaScript objects and controls, see Quickstart: Adding WinJS controls and styles.
Time to complete: 30 minutes.
Instructions
What is pointer input?
By unifying mouse, pen/stylus, and touch input as abstract pointer input you can handle user interactions with your app independently of the type of input device being used.
A pointer object represents a single, unique input "contact" (a PointerPoint) from an input device (such as a mouse, pen/stylus, single finger, or multiple fingers). The system creates a pointer when a contact is first detected and destroys it when the pointer leaves (departs) detection range or is canceled. In the case of multiple devices or multi-touch input, each contact is treated as a unique pointer.
There is an extensive set of pointer-based input APIs that can intercept input data directly from various devices. You can handle pointer events to get common info such as location and device type, and extended info such as pressure and contact geometry. Because, in many cases, apps must respond to different input modes in different ways, specific device properties such as which mouse button a user pressed or whether a user is using the pen eraser tip are also available. For example, touch can be filtered to support interactions such as panning and scrolling while mouse and pen/stylus are typically more suited to precise tasks such as ink and drawing. If your app needs to differentiate between input devices and their capabilities, see Quickstart: Identifying input devices.
If you implement your own interaction support, keep in mind that users expect an intuitive experience involving direct interaction with the UI elements in your app. We recommend that you model your custom interactions on the framework controls to keep things consistent and discoverable. Create custom interactions only if there is a clear, well-defined requirement and basic interactions don't support your scenario.
Create the UI
For this example, we use a rectangle (target) as the target object for pointer input. The color of the target changes when the pointer status changes.
Details for each pointer are displayed in a floating text block that is located adjacent to the pointer and follows any pointer movement. Specific pointer events are reported to the far right of the display. The following screen shot shows the UI for this example.

This is the HTML for this example.
<html> <head> <meta charset="utf-8" /> <title>PointerInput</title> <!-- WinJS references --> <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" /> <script src="//Microsoft.WinJS.1.0/js/base.js"></script> <script src="//Microsoft.WinJSo.1.0/js/ui.js"></script> <!-- PointerInput references --> <link href="/css/default.css" rel="stylesheet" /> <script src="/js/default.js"></script> </head> <body id="app"> <div id="grid"> <div id="targetContainer"> <div id="target"></div> </div> <div id="bottom"> </div> <div id="eventLog"></div> </div> </body> </html>
This is the Cascading Style Sheets (CSS) for this example.
Note Pointer events don't fire during a pan or zoom interaction. You can disable panning and zooming on a region through the CSS properties msTouchAction, overflow, and -ms-content-zooming.
body {
overflow: hidden;
position: relative;
}
#grid {
display: -ms-grid;
height: 100vh; /* 100% of viewport height */
-ms-grid-columns: 4fr 1fr; /* 2 columns */
-ms-grid-rows: 1fr 320px 1fr; /* 3 rows */
-ms-touch-action: none; /* Disable panning and zooming */
}
#top {
-ms-grid-row: 1;
-ms-grid-column: 1;
}
#targetContainer {
-ms-grid-row: 2;
-ms-grid-column: 1;
-ms-grid-row-align: center;
-ms-grid-column-align: center;
-ms-touch-action: none; /* Disable panning and zooming */
}
#bottom {
-ms-grid-row: 3;
-ms-grid-column: 1;
-ms-grid-column-align: center;
}
#eventLog {
-ms-grid-row: 1;
-ms-grid-column: 2;
-ms-grid-row-span: 3;
padding: 10px;
background-color: black;
color: white;
}
#target {
width: 640px;
height: 320px;
border: none;
padding: 0px;
margin: 0px;
-ms-transform-origin: 0% 0%;
-ms-touch-action: none; /* Disable panning and zooming */
}
Listen for pointer events
In most cases, we recommend that you get pointer info through the event argument of the pointer event handlers in your chosen Windows 8 language framework (Windows Store apps using JavaScript, Windows Store apps built for Windows using C++, C#, or Visual Basic, or Windows Store apps built for Windows using DirectX with C++).
If the event argument doesn't expose the pointer details required by your app, you can get access to extended pointer data from the event argument through the getCurrentPoint and getIntermediatePoints methods or currentPoint and intermediatePoints properties. We recommend using the getCurrentPoint and getIntermediatePoints methods as you can specify the context of the pointer data.
The following screen shot shows the target area with mouse pointer details from this example.

Here, we set up the screen location and event listeners for the target area.
First, we declare global variables, initialize the target and pointer detail areas, and identify the event logger.
var
// For this example, we track simultaneous contacts in case the
// number of contacts has reached the maximum supported by the device.
// Depending on the device, additional contacts might be ignored
// (PointerPressed not fired).
numActiveContacts = 0,
// The input target.
target,
// Pointer info (array of pointer Ids and pointer types).
pInfo,
// Target background colors corresponding to various pointer states.
pointerColor = {
hover: "rgb(255, 255, 102)",
down: "rgb(0, 255, 0)",
up: "rgb(255, 0, 0)",
cancel: "rgb(0,0,0)",
out: "rgb(127,127,127)",
over: "rgb(0,0,255)"
},
// The event log (updated on each event).
eventLog;
function initialize() {
// Configure the target.
eventLog = document.getElementById("eventLog");
target = document.getElementById("target");
setTarget();
}
Then, we set up the target object and declare the various pointer event listeners for the target.
function setTarget() {
// Set up the target position and size.
target.style.backgroundColor = pointerColor.out;
// Expando dictionary property to track active contacts. An entry is added
// during MSPointerDown/MSPointerHover/MSPointerOver events and removed
// during MSPointerUp/MSLostPointerCapture/MSPointerCancel/MSPointerOut events.
target.pointers = [];
pInfo = [];
// Declare pointer event handlers.
target.addEventListener("MSPointerDown", onMSPointerDown, true);
target.addEventListener("MSPointerHover", onMSPointerHover, true);
target.addEventListener("MSPointerOver", onMSPointerOver, true);
target.addEventListener("MSPointerUp", onMSPointerUp, true);
target.addEventListener("MSPointerOut", onMSPointerOut, true);
target.addEventListener("MSPointerCancel", onMSPointerCancel, true);
target.addEventListener("MSLostPointerCapture", onMSLostPointerCapture, true);
target.addEventListener("MSPointerMove", onMSPointerMove, true);
target.addEventListener("wheel", onMouseWheel, false);
}
Finally, we set up the pointer detail area.
// Create DIV and insert it into the document hierarchy to display pointer details. function createInfoPop(e) { var infoPop = document.createElement("div"); infoPop.setAttribute("id", "infoPop" + e.pointerId); // Set screen position of DIV. var transform = (new MSCSSMatrix()).translate(e.offsetX + 20, e.offsetY + 20); infoPop.style.msTransform = transform; target.appendChild(infoPop); infoPop.innerText = queryPointer(e); } function updateInfoPop(e) { var infoPop = document.getElementById("infoPop" + e.pointerId); if (infoPop === null) return; // Set screen position of DIV. var transform = (new MSCSSMatrix()).translate(e.offsetX + 20, e.offsetY + 20); infoPop.style.msTransform = transform; infoPop.innerText = queryPointer(e); }
Handle pointer events
Next, we use UI feedback to demonstrate basic pointer event handlers.
-
This handler manages a pointer contact (down, pressed) event. We add the event to the event log, add the pointer to the pointer array used for tracking the pointers of interest, and display the pointer details.
Note MSPointerDown and MSPointerUp events don't always occur in pairs. Your app should listen for and handle any event that might conclude a pointer down action (such as MSPointerUp, MSPointerOut, MSPointerCancel, and MSLostPointerCapture).
// MSPointerDown and MSPointerUp events do not always occur in pairs. // Listen for and handle any event that might conclude a pointer down action // (such as MSPointerUp, MSPointerOut, MSPointerCancel, and MSLostPointerCapture). // For this example, we track the number of contacts in case the // number of contacts has reached the maximum supported by the device. // Depending on the device, additional contacts might be ignored // (PointerPressed not fired). function onMSPointerDown(e) { e.cancelBubble = true; // Check if the number of supported contacts is exceeded. var touchCapabilities = new Windows.Devices.Input.TouchCapabilities(); if ((touchCapabilities.touchPresent != 0) & (numActiveContacts > touchCapabilities.contacts)) { return; } // Update event details and target UI. eventLog.innerText += "\nDown: " + e.pointerId; target.style.backgroundColor = pointerColor.down; // Check if pointer already exists (if hover/over occurred prior to down). for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { return; } } // Push new pointer Id onto expando target pointers array. pInfo.push(e.pointerId, e.pointerType); target.pointers.push(pInfo); // Ensure that the element continues to receive MSPointerEvents // even if the contact moves off the element. // Capturing the current pointer can improve usability by reducing // the touch precision required when interacting with an element. // Note: Only the assigned pointer is affected. target.msSetPointerCapture(e.pointerId); // Display pointer details. createInfoPop(e); } -
This handler manages a pointer hover event. We add the event to the event log, add the pointer to the pointer array, and display the pointer details.
// Fires when a pen device pointer enters detection range. function onMSPointerHover(e) { e.cancelBubble = true; // Update event details and target UI. target.style.backgroundColor = pointerColor.hover; // Check if pointer already exists. for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { updateInfoPop(e); return; } } // Push new pointer Id onto expando target pointers array. pInfo.push(e.pointerId, e.pointerType); target.pointers.push(pInfo); //target.pointers.push(e.pointerId); // Display pointer details. createInfoPop(e); } -
This handler manages a pointer enter (over) event. We add the event to the event log, add the pointer to the pointer array, and display the pointer details.
function onMSPointerOver(e) { e.cancelBubble = true; // Update event details and target UI. eventLog.innerText += "\nOver: " + e.pointerId; if (target.pointers.length === 0) { // Change background color of target when pointer contact detected. if (e.getCurrentPoint(e.currentTarget).isInContact) { // Pointer down occured outside target. target.style.backgroundColor = pointerColor.down; } else { // Pointer down occured inside target. target.style.backgroundColor = pointerColor.over; } } // Check if pointer already exists. for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { return; } } // Push new pointer Id onto expando target pointers array. pInfo.push(e.pointerId, e.pointerType); target.pointers.push(pInfo); // Ensure that the element continues to receive MSPointerEvents // even if the contact moves off the element. // Capturing the current pointer can improve usability by reducing // the touch precision required when interacting with an element. // Note: Only the assigned pointer is affected. target.msSetPointerCapture(e.pointerId); // Display pointer details. createInfoPop(e); } -
This handler manages a pointer move event. We add the event to the event log and update the pointer details. Multiple, simultaneous mouse button clicks are also processed in this handler.
Note Mouse input is associated with a single pointer assigned when mouse input is first detected. Clicking additional mouse buttons (left, wheel, or right) during the interaction creates secondary associations between those buttons and the pointer through the pointer pressed event. The pointer released event is fired only when the last mouse button associated with the interaction (not necessarily the initial button) is released. Because of this exclusive association, other mouse button clicks are routed through the pointer move event.
function onMSPointerMove(e) { e.cancelBubble = true; // Multiple, simultaneous mouse button inputs are processed here. // Mouse input is associated with a single pointer assigned when // mouse input is first detected. // Clicking additional mouse buttons (left, wheel, or right) during // the interaction creates secondary associations between those buttons // and the pointer through the pointer pressed event. // The pointer released event is fired only when the last mouse button // associated with the interaction (not necessarily the initial button) // is released. // Because of this exclusive association, other mouse button clicks are // routed through the pointer move event. if (e.pointerType == e.MSPOINTER_TYPE_MOUSE) { // Mouse button states are extended PointerPoint properties. var pt = e.getCurrentPoint(e.currentTarget); var ptProperties = pt.properties; if (ptProperties.isLeftButtonPressed) { eventLog.innerText += "\nLeft button: " + e.pointerId; } if (ptProperties.isMiddleButtonPressed) { eventLog.innerText += "\nWheel button: " + e.pointerId; } if (ptProperties.isRightButtonPressed) { eventLog.innerText += "\nRight button: " + e.pointerId; } } // Display pointer details. updateInfoPop(e); } -
This handler manages a mouse wheel event (rotation). We add the event to the event log, add the pointer to the pointer array (if necessary), and display the pointer details.
function onMouseWheel(e) { // Check if a mouse pointer already exists. for (var i in target.pointers) { // Ensure existing pointer type is e.MSPOINTER_TYPE_MOUSE (4). if (target.pointers[i][1] === 4) { e.pointerId = target.pointers[i][0]; break; } } eventLog.innerText += "\nMouse wheel: " + e.pointerId; // For this example, we fire a corresponding pointer down event. onMSPointerDown(e); }
-
This handler manages a pointer departure (up) event. We add the event to the event log, remove the pointer from the pointer array, and update the pointer details.
function onMSPointerUp(e) { e.cancelBubble = true; // Update event details. eventLog.innerText += "\nUp: " + e.pointerId; // If event source is mouse pointer and the pointer is still // over the target, retain pointer and pointer details. // Return without removing pointer from pointers dictionary. // For this example, we assume a maximum of one mouse pointer. if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE) & (document.elementFromPoint(e.x, e.y) === target)) { target.style.backgroundColor = pointerColor.up; return; } // Ensure capture is released on a pointer up event. target.msReleasePointerCapture(e.pointerId); // Remove pointer from pointers dictionary. for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { target.pointers.splice(i, 1); var pointerInfoPop = document.getElementById("infoPop" + e.pointerId); if (pointerInfoPop === null) return; pointerInfoPop.removeNode(true); } } // Update target UI. if (target.pointers.length === 0) { target.style.backgroundColor = pointerColor.up; } } -
This handler manages a pointer departure (out) event. We add the event to the event log, remove the pointer from the pointer array, and update the pointer details.
// Fires when pointer departs target detection (move and after up) or // a pointer in hover state departs detection range without making contact. // Note: Pointer capture is maintained until pointer up event. function onMSPointerOut(e) { e.cancelBubble = true; // Update event details. eventLog.innerText += "\nPointer out: " + e.pointerId; // Remove pointer from pointers dictionary. for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { target.pointers.splice(i, 1); var pointerInfoPop = document.getElementById("infoPop" + e.pointerId); if (pointerInfoPop === null) return; pointerInfoPop.removeNode(true); // Update target UI. if (target.pointers.length === 0) { target.style.backgroundColor = pointerColor.out; } } } } -
This handler manages a pointer cancel event. We add the event to the event log, remove the pointer from the pointer array, and update the pointer details.
// Fires for for various reasons, including: // - A touch contact is canceled by a pen coming into range of the surface. // - The device doesn't report an active contact for more than 100ms. // - The desktop is locked or the user logged off. // - The number of simultaneous contacts exceeded the number supported by the device. function onMSPointerCancel(e) { e.cancelBubble = true; // Update event details. eventLog.innerText += "\nPointer canceled: " + e.pointerId; // Ensure capture is released on a pointer cancel event. target.msReleasePointerCapture(e.pointerId); // Update target UI. if (target.pointers.length === 0) { target.style.backgroundColor = pointerColor.cancel; } // Remove pointer from pointers dictionary. for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { target.pointers.splice(i, 1); var pointerInfoPop = document.getElementById("infoPop" + e.pointerId); if (pointerInfoPop === null) return; pointerInfoPop.removeNode(true); } } } -
This handler manages a lost pointer capture event. We add the event to the event log, remove the pointer from the pointer array, and update the pointer details.
Note MSLostPointerCapture can occur instead of MSPointerUp. Pointer capture can be lost because of user interactions or because another pointer was captured programmatically or the current pointer capture was deliberately released.
// Fires for for various reasons, including: // - User interactions // - Programmatic caputre of another pointer // - Captured pointer was deliberately released // MSLostPointerCapture can fire instead of MSPointerUp. function onMSLostPointerCapture(e) { e.cancelBubble = true; // Update event details. eventLog.innerText += "\nLost pointer capture: " + e.pointerId; // We need the device type to handle lost pointer capture from mouse input. // Use the getCurrentPoint method over currentPoint property to ensure // the coordinate space is in relation to the target element. // Note: getCurrentPoint and currentPoint are only available in the // local compartment, they are not available in the web compartment. var ptTarget = e.getCurrentPoint(e.currentTarget); var ptContainer = e.getCurrentPoint(app); // If event source is mouse pointer and the pointer is still over // the target, retain pointer and pointer details. // For this example, we assume only one mouse pointer. if ((ptTarget.pointerDevice.pointerDeviceType === Windows.Devices.Input.PointerDeviceType.mouse) & (document.elementFromPoint(ptContainer.position.x, ptContainer.position.y) === target)) { target.msSetPointerCapture(e.pointerId); return; } // Remove pointer from pointers dictionary. for (var i in target.pointers) { if (target.pointers[i][0] === e.pointerId) { target.pointers.splice(i, 1); var pointerInfoPop = document.getElementById("infoPop" + e.pointerId); if (pointerInfoPop === null) return; pointerInfoPop.removeNode(true); } } // Update target UI. if (target.pointers.length === 0) { target.style.backgroundColor = pointerColor.cancel; } }
Get pointer properties
The language framework you choose for your app dictates how you get pointer properties. Windows Store apps using JavaScript expose many properties directly through the pointer event object. As noted earlier, additional pointer info can be obtained through the getCurrentPoint and getIntermediatePoints methods or the currentPoint and intermediatePoints properties of the event argument. We recommend using the getCurrentPoint and getIntermediatePoints methods as you can specify the context of the pointer data.
Here we query various pointer properties directly from the event object and extended properties available only through PointerPoint and PointerPointProperties objects.
// Get extended pointer data. function queryPointer(e) { // We get the extended pointer info through the getCurrentPoint method // of the event argument. (We recommend using getCurrentPoint // to ensure the coordinate space is in relation to the target.) // Note: getCurrentPoint and currentPoint are only available in the // local compartment, they are not available in the web compartment. var ptTargetProperties = e.getCurrentPoint(e.currentTarget).properties; var details = "Pointer Id: " + e.pointerId; switch (e.pointerType) { case e.MSPOINTER_TYPE_MOUSE: details += "\nPointer type: mouse"; details += "\nLeft button: " + ptTargetProperties.isLeftButtonPressed; details += "\nRight button: " + ptTargetProperties.isRightButtonPressed; details += "\nWheel button: " + ptTargetProperties.isMiddleButtonPressed; details += "\nX1 button: " + ptTargetProperties.isXButton1Pressed; details += "\nX2 button: " + ptTargetProperties.isXButton2Pressed; break; case e.MSPOINTER_TYPE_PEN: details += "\nPointer type: pen"; if (ptProperties.isInContact) { details += "\nPressure: " + ptTargetProperties.pressure; details += "\nrotation: " + ptTargetProperties.rotation; details += "\nTilt X: " + ptTargetProperties.tiltX; details += "\nTilt Y: " + ptTargetProperties.tiltY; details += "\nBarrel button pressed: " + ptTargetProperties.isBarrelButtonPressed; } break; case e.MSPOINTER_TYPE_TOUCH: details += "\nPointer type: touch"; details += "\nPressure: " + ptTargetProperties.pressure; details += "\nrotation: " + ptTargetProperties.rotation; details += "\nTilt X: " + ptTargetProperties.tiltX; details += "\nTilt Y: " + ptTargetProperties.tiltY; break; default: details += "\nPointer type: " + "n/a"; break; } details += "\nPointer location (target): " + e.offsetX + ", " + e.offsetY; details += "\nPointer location (screen): " + e.screenX + ", " + e.screenY; return details; }
See Related topics at the bottom of this page for links to more complex samples.
Complete example
Summary and next steps
In this Quickstart, you learned about pointer input in Windows Store apps using JavaScript.
Pointer events are useful for managing simple interactions such as tap, slide, and other device-specific interactions including input from secondary mouse buttons, mouse wheel, pen barrel button, and pen eraser.
For handling more elaborate interactions, such as the gestures described in the Windows 8 touch language, see Quickstart: DOM gestures and manipulations, Quickstart: Static gestures, and Quickstart: Manipulation gestures.
For more info on the Windows 8 touch language, see Touch interaction design.
Related topics
- Conceptual
- Responding to user interaction
- Quickstart: DOM gestures and manipulations
- Quickstart: Static gestures
- Quickstart: Manipulation gestures
- Developing Windows Store apps (JavaScript and HTML)
- Touch interaction design
- Reference
- Windows.Devices.Input
- Windows.UI.Core
- Windows.UI.Input
- Samples (DOM)
- HTML scrolling, panning and zooming sample
- Input: DOM pointer event handling sample
- Input: Instantiable gestures sample
- Samples (Windows Store app APIs)
- Input: Ink sample
- Input: Manipulations and gestures (JavaScript) sample
- Input: Simplified ink sample
- Input: Windows 8 gestures sample
- Input: XAML user input events sample
- XAML scrolling, panning, and zooming sample
- Samples (DirectX)
- DirectX touch input sample
- Input: Manipulations and gestures (C++) sample
- Input: Touch hit testing sample
Build date: 11/29/2012