Language: JavaScript and HTML | VB/C#/C++ and XAML

Quickstart: Pointers (Windows Runtime apps using JavaScript and HTML)

Applies to Windows and Windows Phone

Touch, mouse, and pen/stylus interactions are received, processed, and managed as pointer input in apps using JavaScript.

Updates for Windows 8.1:  Windows 8.1 introduces a number of updates and improvements to the pointer input APIs. See API changes for Windows 8.1 for more info.

If you're new to developing apps using JavaScript:  Have a look through these topics to get familiar with the technologies discussed here.

Create your first Windows Store app using JavaScript

Roadmap for Windows Store apps using JavaScript

Learn about events with Quickstart: adding HTML controls and handling events

App features, start to finish:  

Explore this functionality in more depth as part of our App features, start to finish series

User interaction, start to finish (HTML)

User interaction customization, start to finish (HTML)

User experience guidelines:  

The platform control libraries (HTML and XAML) provide the full Windows user interaction experience, including standard interactions, animated physics effects, and visual feedback. If you don't need customized interaction support, use these built-in controls.

If the platform controls are not sufficient, these user interaction guidelines can help you provide a compelling and immersive interaction experience that is consistent across input modes. These guidelines are primarily focused on touch input, but they are still relevant for touchpad, mouse, keyboard, and stylus input.

Samples:  See this functionality in action in our app samples.

User interaction customization, start to finish sample

HTML scrolling, panning and zooming sample

Input: DOM pointer event handling sample

Input: Instantiable gestures sample

Input: Ink sample

Input: Manipulations and gestures (JavaScript) sample

Input: Simplified ink sample

Objective: To learn how to listen for and handle pointer input.

Prerequisites

We assume that you can create a basic app using JavaScript that uses the Windows Library for JavaScript template.

To complete this tutorial, you need to:

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.

Screen shot of the example app UI.

This is the HTML for this example.


<html>
<head>
    <meta charset="utf-8" />
    <title>PointerInput</title>
    
    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.2.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 */
    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;
    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%;
    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 language framework.

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.

Screenshot of the pointer target area.

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,

    // 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 pointerdown/pointerhover/pointerover events and removed 
        // during pointerup/lostpointercapture/pointercancel/pointerout events.

        target.pointers = [];

        // Declare pointer event handlers.
        target.addEventListener("pointerdown", onPointerDown, true);
        target.addEventListener("pointerover", onPointerOver, true);
        target.addEventListener("pointerup", onPointerUp, true);
        target.addEventListener("pointerout", onPointerOut, true);
        target.addEventListener("pointercancel", onPointerCancel, true);
        target.addEventListener("lostpointercapture", onLostPointerCapture, true);
        target.addEventListener("pointermove", onPointerMove, 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  pointerdown and pointerup 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 pointerup, pointerout, pointercancel, and lostpointercapture).

    
        // pointerdown and pointerup events do not always occur in pairs. 
        // Listen for and handle any event that might conclude a pointer down action 
        // (such as pointerup, pointerout, pointercancel, and lostpointercapture).
        // 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 onPointerDown(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 (parseInt(i) === e.pointerId) {
                    return;
                }
            }
    
            // Push new pointer Id onto expando target pointers array.
            target.pointers[e.pointerId] = e.pointerType;
    
            // Ensure that the element continues to receive PointerEvents 
            // 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.setPointerCapture(e.pointerId);
    
            // Display pointer details.
            createInfoPop(e);
        }
    
    
    
  • This handler manages a pointer enter (over) event for a pointer that is in contact and moved within the boundary of the target. We add the event to the event log, add the pointer to the pointer array, and display the pointer details.

    See the pointermove event for handling the hover state of a pointer that is not in contact but is within the boundary of the target (typically a pen/stylus device).

    
    function onPointerOver(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 (parseInt(i) === e.pointerId) {
                return;
            }
        }
    
        // Push new pointer Id onto expando target pointers array.
        target.pointers[e.pointerId] = e.pointerType;
    
        // Ensure that the element continues to receive PointerEvents 
        // 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.setPointerCapture(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 (for hover, we also add the pointer to the pointer array).

    MSPointerHover has been deprecated in Windows 8.1. Use pointermove and the IsInContact pointer property to determine hover state.

    Note   Multiple, simultaneous mouse button clicks are also processed in this handler. 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 onPointerMove(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 == "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;
                }
            }
            // Handle hover state of a pointer that is not in contact but is within 
            // the boundary of the target (typically a pen/stylus device). 
            if (e.pointerType == "pen") {
                var pt = e.getCurrentPoint(e.currentTarget);
                if (pt.isInContact == false) {
                    // Update event details and target UI.
                    target.style.backgroundColor = pointerColor.hover;
                    eventLog.innerText = "\nHover: " + e.pointerId;
    
                    // Check if pointer already exists.
                    for (var i in target.pointers) {
                        if (parseInt(i) === e.pointerId) {
                            updateInfoPop(e);
                            return;
                        }
                    }
    
                    // Push new pointer Id onto expando target pointers array.
                    target.pointers[e.pointerId] = e.pointerType;
    
                    // Ensure that the element continues to receive PointerEvents 
                    // 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.setPointerCapture(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.
        onPointerDown(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 onPointerUp(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 === "mouse") &
                (document.elementFromPoint(e.x, e.y) === target)) {
                target.style.backgroundColor = pointerColor.up;
                return;
            }
    
            // Ensure capture is released on a pointer up event.
            target.releasePointerCapture(e.pointerId);
    
            // Remove pointer from pointers dictionary.
            var targetPointers = target.pointers;
            for (var i in targetPointers) {
                if (parseInt(i) === 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 onPointerOut(e) {
            e.cancelBubble = true;
    
            // Update event details.
            eventLog.innerText += "\nPointer out: " + e.pointerId;
    
            // Remove pointer from pointers dictionary.
            var targetPointers = target.pointers;
            for (var i in targetPointers) {
                if (parseInt(i) === 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 onPointerCancel(e) {
            e.cancelBubble = true;
    
            // Update event details.
            eventLog.innerText += "\nPointer canceled: " + e.pointerId;
    
            // Ensure capture is released on a pointer cancel event.
            target.releasePointerCapture(e.pointerId);
    
            // Update target UI.
            if (target.pointers.length === 0) {
                target.style.backgroundColor = pointerColor.cancel;
            }
    
            // Remove pointer from pointers dictionary.
            var targetPointers = target.pointers;
            for (var i in targetPointers) {
                if (parseInt(i) === 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  lostpointercapture can occur instead of pointerup. 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
        // lostpointercapture can fire instead of pointerup. 
        function onLostPointerCapture(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.setPointerCapture(e.pointerId);
                return;
            }
    
            // Remove pointer from pointers dictionary.
            var targetPointers = target.pointers;
            for (var i in targetPointers) {
                if (parseInt(i) === 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. Many properties are exposed 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 pt = e.getCurrentPoint(e.currentTarget);
    var ptTargetProperties = pt.properties;

    var details = "Pointer Id: " + e.pointerId;
    switch (e.pointerType) {
        case "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 "pen":
            details += "\nPointer type: pen";
            if (pt.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 "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

See Pointers complete code.

Summary and next steps

In this Quickstart, you learned about pointer input in 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

Developers
Responding to user interaction
Developing Windows Store apps (JavaScript and HTML)
Quickstart: DOM gestures and manipulations
Quickstart: Static gestures
Quickstart: Manipulation gestures
Designers
Touch interaction design

 

 

Show:
© 2014 Microsoft. All rights reserved.