Git Product home page Git Product logo

pep's Introduction

PEP has now entered emeritus status at the OpenJS Foundation. This repository is now archived.

Pointer Events Polyfill - making pointer events usable today

PEP logo

PEP polyfills pointer events in all browsers that haven't yet implemented them, providing a unified, responsive input model for all devices and input types. You can read more about pointer events below.

Project status

PEP development ceased in 2018. A few minor maintenance patches have been pushed since then, and the latest version (0.5.3) has been published to npm. However, native support for Pointer Events is relatively good in most browsers at this point - see caniuse: Pointer Events. This project is now archived.

Getting Started

  1. Place the PEP script in the document head
  • <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
  1. By default, no pointer events are sent from an element. This maximizes the possibility that a browser can deliver smooth scrolling and jank-free gestures. If you want to receive events, you must set the touch-action property of that element. Set up some elements to create events with the touch-action attribute.

  2. Listen for the desired events

  • pointermove: a pointer moves, similar to touchmove or mousemove.
  • pointerdown: a pointer is activated, or a device button held.
  • pointerup: a pointer is deactivated, or a device button released.
  • pointerover: a pointer has moved onto an element.
  • pointerout: a pointer is no longer on an element it once was.
  • pointerenter: a pointer enters the bounding box of an element.
  • pointerleave: a pointer leaves the bounding box of an element.
  • pointercancel: a pointer will no longer generate events.
  1. As elements come and go, or have their touch-action attribute changed, they will send the proper set of pointer events.
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>PEP (Pointer Events Polyfill)</title>
  <meta name="viewport" content="width=device-width">
  <!-- include PEP -->
  <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
</head>
<body>
<button id="b" touch-action="none">Test button!</button>
<p><output id="o"></output></p>
<script>
document.getElementById( "b" ).addEventListener( "pointerdown", function( e ) {
  document.getElementById( "o" ).innerHTML = "that was a " +
    e.pointerType + " " + e.type + " on a "+ e.target.nodeName;
} );
</script>
</body>
</html>

See also the examples in the W3C Pointer Events Specification and our own samples for using PEP.

Using PEP as a module

npm install pepjs
import 'pepjs'

Using PEP with jQuery

You can use pointer events with jQuery and PEP:

<div id="canvas" touch-action="none"></div>
<script src="pep.dist.js"></script>
<script src="jquery.js"></script>
<script>
$("#canvas").on("pointermove", function(event) {
  draw(event);
});
</script>

Check out this jsbin demo for a full demo.

jQuery doesn't copy all properties from the original event object to the event object provided in the event handler. You can find a list of copied and normalized properties on the jQuery API docs. To access any other original properties, use event.originalEvent.

Using PEP with React

As of version 16.4, React comes with first class support for pointer events. To use pointer events on unsupported browsers, you can include PEP before mounting your React application. You can also use the touch-action property on any JSX node:

export function Pointable() {
  return <div touch-action="none" onPointerDown={(e) => console.log(e)} /> 
}

Why Pointer Events?

Mouse events and touch events are fundamentally different beasts in browsers today, and that makes it hard to write cross-platform apps.

For example, a simple finger paint app needs plenty of work to behave correctly with mouse and touch:

Current platforms that implement touch events also provide mouse events for backward compatibility; however, only a subset of mouse events are fired and the semantics are changed.

  • Mouse events are only fired after the touch sequence ends.
  • Mouse events are not fired on elements without a click event handler. One must be attached by default, or directly on the element with onclick.
  • click events are not fired if the content of the page changes in a mousemove or mouseover event.
  • click events are fired 300ms after the touch sequence ends.
  • More information: Apple Developer Documentation.

Additionally, touch events are sent only to the element that received the touchstart. This is fundamentally different than mouse events, which fire on the element that is under the mouse. To make them behave similarly, touch events need to be retargeted with document.elementFromPoint.

These incompatibilities lead to applications having to listen to 2 sets of events, mouse on desktop and touch for mobile.

This forked interaction experience is cumbersome and hard to maintain.

Instead, there should exist a set of events that are normalized such that they behave exactly the same, no matter the source: touch, mouse, stylus, skull implant, etc. To do this right, this normalized event system needs to be available for all the web platform to use.

Thus, Pointer Events!

Polyfill Limitations

touch-action

According to the spec, the touch-action CSS property controls whether an element will perform a "default action" such as scrolling, or receive a continuous stream of pointer events.

Due to the difficult nature of polyfilling new CSS properties, this library uses a touch-action attribute instead. For PEP to work correctly, you will therefore need to include touch-action="..." attributes in your HTML that mirror any touch-action:... properties you have in your CSS.

<style>
  div#foo { touch-action: none; }
</style>
...
<div id="foo" touch-action="none"> ... </div>

Run time changes involving the touch-action attribute are monitored using Mutation Observers for maximum flexibility.

Touches will not generate events unless inside of an area that has a valid touch-action attribute defined. This is to maintain composition scrolling optimizations where possible.

Capturing Phase

PEP does not currently polyfill the capturing phase for pointer events.

navigator.maxTouchPoints

As the information necessary to populate navigator.maxTouchPoints is not available in browsers that do not natively implement pointer events, PEP sets the value to 0, which is "the minimum number guaranteed to be recognized" as required by the specification.

Browser Compatibility

PEP should work on IE 10+ and the latest versions of Chrome, Safari, Firefox, and Opera. In any browser implementing pointer events natively (detected by checking for window.PointerEvent), PEP won't do anything.

Building PEP

If you want to build PEP yourself from source, you'll need to install Node.js and run the following commands:

# Install all dependencies
npm install

# Build PEP
npm run build

When the build completes, the generated files will be available in the dist/ directory.

NOTE: Running the demos requires building PEP.

pep's People

Contributors

ahocevar avatar appsforartists avatar bethge avatar csnover avatar dependabot[bot] avatar dfreedm avatar drcmda avatar ebidel avatar frankiefu avatar jdalton avatar jzaefferer avatar kborchers avatar longtian avatar marcello3d avatar marlonmarcello avatar mike-marcacci avatar milllertime avatar mmariano avatar patrickhlauke avatar philipp-spiess avatar reski78 avatar roblarsen avatar scottgonzalez avatar steditor avatar stuartpb avatar tbuckley avatar thomas-darling avatar tjsavage avatar wbinnssmith avatar zoechi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pep's Issues

Should there be a mechanism to cancel touchstart events?

Today it looks like PointerEvents only ever calls preventDefault on touchmove events.

There are scenarios where developers call preventDefault on touchstart today. The most compelling scenario is perhaps as a way to disable the long press affordance we have in chrome desktop before dispatching a contextmenu event. If Chrome were to implement pointer events natively, we'd probably make cancelling a pointerdown do that (although a CSS-based mechanism may be better).

Should PointerEvent have some way of cancelling the underlying touch event? Maybe even a 'cancelOriginalEvent()' method or something - would be a bit of a hack, but probably better than the alternative of the developer having to add a touch event listener.

There's an equivalent mouse scenarios as well that I haven't looked into as deeply. Eg. calling preventDefault on mouseDown disables text selection, and so should calling it on pointerdown. Does that already work?

offsetX, offsetY are not correct in Firefox, Opera, Chrome mobile

When pointerevents fire the only way to get the location relative to the target element is offsetX and offsetY.

These values appear to be 0 in some browsers.

Working correctly:

  • Firefox for desktop
  • Safari for desktop

Wrong values:

  • Chrome beta for Android

Always zero:

  • Firefox
  • Opera

Not tested:

  • Any IE
  • Mobile Safari

Implement support for touch-action=auto

At the moment, pointer events are only sent when touch-action=none is set on an element. We don't think there's any good reason why we couldn't also implement the auto model when the user has explicitly opted into it. Enabling pointer events support on scrollable divs has a performance impact (forces scrolling to be blocked on the main thread), so we can't do it by default. But a touch-action='auto' attribute is a good signal that pointer events support should be enabled.

See this thread for more details: http://lists.w3.org/Archives/Public/public-pointer-events/2013AprJun/0162.html

Clean up Mouse to listen on document

Currently, the Mouse listeners are per element. There is no real reason for this, and was done only to share code paths with Touch listeners.

Enter/leave events aren't emitted on a container when the pointer enters directly into a child element

Not sure if this is the intended behaviour (please ignore me if it is!), but I think I've stumbled on a bug with the enter/leave events:

I've got a fixed-size container <div> which has a single child <div> of exactly the same dimensions. If I move the mouse (or other pointer) into these elements, only the child emits the pointerenter event, even though both elements have been entered. Similarly, only the child emits the pointerleave event when the mouse vacates the elements.

The problem can be replicated in the "simple" example, by adding the following CSS

#enterleave {
    height: 300px;
    width: 300px;
    left: 0;
    top: 0;
}

The in/out events seem to work fine.

Tested in all major browsers on Win7, and IE10 on Win8 with mouse, touch, and stylus.

option to not automatically look for touch-action="none" for potential performance improvement

According to http://updates.html5rocks.com/2012/02/Detect-DOM-changes-with-Mutation-Observers mutation observers can have performance issues.

Could there be a performance boost if we don't watch DOM changes and only register elements via PointerEventsPolyfill.setTouchAction(canvas, 'none');?

I'm not sure the best way to set this kind of option. These seem a little messy:

// after script has loaded but before DOM ready
PointerEventsPolyfill.install.noMutationObservers = true;
<body data-pointer-events-pollyfill="{noMutationObservers: true}">

Important: I have not done any performance testing and I do not know if this is even an issue. This is purely based on an article that could be out of date.

Inaccurate explanation of spec for touch-action css property

According to the spec, the touch-action css property determines if an element will receive PointerEvents.

Unless I'm misreading the spec and my own tests, this explanation is not quite accurate. Pointer Events are always fired on elements - touch-action only defines (per spec) what happens to default behavior, i.e. if the event is consumed entirely (similar to preventDefault-ing a touchstart/touchmove/touchend) or if it will fire and then carry on with its normal default behavior.

Specifically, only elements with the styling that have the touch-action value of none will receive events.

Following on from the above, this is also not quite accurate. values of pan-x and pan-y filter out which movement can be consumed (i.e. the event fires, but still triggers default browser behavior for x or y movement), so it's not just value of none that is relevant.

In short, I think the sentence in the explanation should be changed to reflect the fact that, rather than being based on the spec, the limitations are design choices done by this polyfill itself (as is hinted at later on with "This is to maintain compositiong scrolling optimizations where possible"). It's a valid reason to limit the polyfill, but it's confusing that it's implied this is in line with the spec.

TypeError: cannot use the given object as a weak map key

Hi,

I use this js librairy in an angularjs project and when I try it in firefox 21.0, I have this error :
TypeError: cannot use the given object as a weak map key
Line 611 : this.targets.set(e, this.targets.get(inEvent) || inEvent.target);

capture du 2013-11-26 11 13 33

Could you fix it, cause in the README.md you annonce that fully support Firefox 14+ ?

detail should be set for pointerType="touch"?

For pointer event that is generated from touch, it seems the detail field is always equal to 0. But for pointer event that is generated from mouse, it has correct detail field (click count).

For example: if double click mouse, the detail of first pointerdown, pointerup equal to 1 and the second equal to 2. If I double tap, the detail of pointerdown and pointerup all equal to 0.

I am not really sure if this is bug or intended. The touch event itself does not have a detail field. If this is not a bug, please feel free to close it.

upgrade to grunt 0.4

It shouldn't be much work but will make it easier for everyone else who's already upgraded.

Non-standard touch-action 'scroll' should be merged into 'auto'

We support an explicit 'scroll' value for touch-action, but I think it should ideally be unnecessary. Ideally this is the same behavior you'd get from touch-action=auto (which should be the default on any region where pointer event support has been enabled). Once auto exists (issue 68), perhaps 'scroll' should be deprecated?

Missing pointerup events when using touch input.

I'm running into an error with touch to pointerEvents, where pointer up's are not being generated if the content being touched changes after the touch has started. Moreover Polymer sometimes crashes with the error message: "Object # has no method 'elementFromPoint'"

Device info:
Chromebook Pixel
Browser: Chromium 33.0.1735.0 (Developer Build 239803)

For an example where the error message consistently appears:

  1. Open http://jsfiddle.net/6q9ws/12
  2. Touch the grey box.

For the missing pounterUp event:

  1. Open http://jsfiddle.net/6q9ws/13/
  2. Touch the grey box

Expected output:
touch
mode a:down
touch
mode b:up

Actual ouput:
touch
mode a:down

Want to use samples but don't have mutation_summary.js. Help?

Hi,

This looks really interesting and the future but I can't use the samples because third_party/mutation_summary/mutation_summary.js is not part of the repository and all the samples reference this file.

Can you add this file to the repo?

Thanks
Gareth

Scrolling broken when touch-action and touch events are supported

To repro:
Use a browser that supports both touch-events and touch-action (for example chromium with https://codereview.chromium.org/108673003/ applied and --enable-experimental-web-platform-features set, soon also Firefox). Then try a test page that uses PointerEvents like samples/scroller/index.html. Notice that you can't scroll anything anymore.

I believe this is because touch.js calls scrollType.set in the !HAS_TOUCH_ACTION code paths, but not in the HAS_TOUCH_ACTION codepaths. With HAS_TOUCH_ACTION shouldScroll appears to always return false because scrollType is unpopulated, and so all touchmove events are consumed. It looks like you can simulate this same state by forcing HAS_TOUCH_ACTION to be true in touch.js.

It's not immediately obvious to me what the right fix is, but I think the reasoning behind this code is flawed. It looks like when touch-action is supported it's using a simpler path of always registering touch-event handlers, otherwise it adds the handlers only when required by the touch-action. I believe this is to avoid blocking composited scrolling in the common case where scrolling is permitted. However browsers that support JUST touch-action and touch-events will still have this problem - for compatibility if there's a touch handler then scrolling must be blocked on the handler. Firefox intends to ship this way, so PointerEvents should not be disabling the touch handler optimization just because touch-action is supported.

In Chrome we plan to address the scroll-blocking issue through an additional feature on top of touch-action: 'touch-action-delay'. When 'touch-action-delay: none' is in effect then the presence of a touch handler will not impact scroll performance. So I think the correct fix here is to change HAS_TOUCH_ACTION to HAS_TOUCH_ACTION_DELAY (and fix the issue where scrollType isn't set).

touch-action support in chrome is imminent (http://crbug.com/241964), but touch-action-delay support is still a ways out (http://crbug.com/329559). So it's fine if we want to make the trivial change first (make behavior conditional on touch-action-delay instead of touch-action) and worry about the larger issue (setting the scrollType correctly) later.

PointerEvents always adds hit testing overhead to touchmove events

TouchEvents follows an implicit capture model that makes it possible to avoid hit tests on touchmove events. PointerEvents uses an explicit capture model. Ideally when an application has requested capture, pointermove events should again not require hit tests (and so be essentially as fast as touchmove).

Unfortunately there's an edge case in the spect that requires some limited hit testing during capture. I'll try to get the spec changed here (See http://lists.w3.org/Archives/Public/public-pointer-events/2014JanMar/0056.html). If I can't, then we can probably work around this by somehow doing a simpler hit test than relying on a full elementFromPoint. If I succeed, then we need to change the polyfill to avoid doing any hit tests (elementFromPoint calls) on pointers that have capture.

Take advantage of touch-action support on Chrome when available

Chrome is getting experimental native support for touch-action (http://crbug.com/241964), with a primary goal of enabling better pointer events polyfills. This will only be available behind the --enable-experimental-webkit-features flag for awhile, so it won't be a real user scenario anytime soon. But still, to validate the work, I will update the polymer polyfill to take advantage of native touch-action support when present.

pointer event returns incorrect pointerType on touch

So far I only tested on nexus 7 but it appears that pointer events return pinterType = "mouse" on touch.

Is there any other way of detecting if a pointer event has been triggered with a mouse or touch device? I noticed that pointer.Id for mouse used to be 1 and for touch 2 and higher. In the most recent polymer version, touch starts with 1

Factor touch code out from mouse code to make IE6 work easier

None of the support for touch input is necessary for IE6 support, so to make that easier Daniel is going to tease some of the mouse and touch code apart so that the jQuery guys can focus their IE6 efforts on just the vastly-simpler mouse code.

Consider parsing CSS for touch-action

Since polyfilling CSS is hard, we look for touch-action as an HTML attribute instead of a CSS property. This is probably the right trade-off in most cases (until browsers have native support for at least parsing the touch-action CSS property).

But perhaps we should consider having an option (not included in the build by default?) to make a best effort at parsing from CSS like other CSS polyfills (eg. using http://www.glazman.org/JSCSSP/), just to have the ability to be maximally spec compliant.

Missing build files

I'm investigating some of the multitouch pointer libraries and this one looks promising, but it's a bit of a hassle to test with -- I need to clone the repo and build with grunt first.

Providing the build files that grunt generates (.min, .dev) would make it easier for people to do some quick tests without having to clone, install node, install grunt + extras, build.

Cheers.

Pointer events not firing when polymer script loaded via script loaders

Polymer relies on DOMContentLoaded to start binding events. However, this is invoked only when the document.readystate is not complete.

However, in cases where polymer is loaded via script loaders, the document.readyState is interactive and DOMContentLoaded has already fired when the polymyer code executed. Hence, this statement is never called.

How about checking if the readyState is not interactive yet, and if it is not, add DOMContent loaded. If it has already hit the 'interactive' state, simply call installNewSubtree (like we do inside the DOMContentLoaded handler) ?

P.S: Discovered this when trying to use polymer as a cordova plugin that is loaded using cordova.require,

Opera 12: DOMException: NOT_SUPPORTED_ERR

This can be repeated using the samples such as PointerEvents/samples/tracker/index.html with Opera 12:

Moving the mouse on the page throws these exceptions. The pointer events are not triggered.

Uncaught exception: DOMException: NOT_SUPPORTED_ERR

Error thrown at line 103, column 2 in PointerEvent(inType, inDict) in http://localhost:7777/js/components/PointerEvents/src/PointerEvent.js:
    Object.defineProperties(e, {
called from line 129, column 6 in <anonymous function: makeEvent>(inType, inEvent) in http://localhost:7777/js/components/PointerEvents/src/dispatcher.js:
    var e = new PointerEvent(inType, inEvent);
called from line 135, column 6 in <anonymous function: fireEvent>(inType, inEvent) in http://localhost:7777/js/components/PointerEvents/src/dispatcher.js:
    var e = this.makeEvent(inType, inEvent);
called from line 78, column 6 in <anonymous function: over>(inEvent) in http://localhost:7777/js/components/PointerEvents/src/dispatcher.js:
    this.fireEvent('pointerover', inEvent)
called from line 289, column 8 in <anonymous function: mouseover>(inEvent) in http://localhost:7777/js/components/PointerEvents/src/platform-events.js:
    dispatcher.over(e);
called as bound function from line 97, column 8 in <anonymous function: eventHandler>(inEvent) in http://localhost:7777/js/components/PointerEvents/src/dispatcher.js:
    fn(inEvent);
Uncaught exception: DOMException: NOT_SUPPORTED_ERR

I'm using Opera 12.14 in OS X 10.8.2 using a mouse. Opera is listed a supported platform in the Readme so I wouldn't expect there to be any problems.

Allow for limited Mouse Events in Touch Environments

To facilitate a limited usage of PointerEvents by devices with Mouse AND Touch systems, we could allow for MouseEvents to generate PointerEvents if no touches are on the screen.

This avoids having to preventDefault the touches to prevent simulated Mouse events from them.

pointerdown event fired twice on touch devices which simulate mouse events

On iPad, pointerdown event is fired twice because both mouse and touch events are registered in https://github.com/toolkitchen/PointerEvents/blob/master/src/platform-events.js#L240

I don't know about other devices but at least iPad sends the events like this:
http://stackoverflow.com/a/8505370

I made a "fix" for this: https://github.com/kimmobrunfeldt/PointerEvents/commit/e8e5d010c1100cbf4387f9ea29330f4cdbcf83fb but that disables the support for having both touch and mouse working together.

Two pointerdown events shouldn't be fired.

preventDefault() on PointerEvents not connected with originating event

I'm using your PointerEvents library and I'm having an issue on Chrome on Android. The issue is that if you do a long touch, Chrome generates both a touch event and a mouse event unless you call preventDefault() (Chrome bug: http://code.google.com/p/chromium/issues/detail?id=119216). If you then move your finger, in my case it results in a zoom instead of a pan because it thinks there are two touch points (one touch, then a finger). I'm calling preventDefault() on the PointerEvent, but that isn't connected with the original event and there isn't a clear way to get to the original event.

As a work-around -- that is probably not appropriate for many people, but works in my specific case -- is to explicitly call preventDefault on the original event in platform-events.js touchstart():

touchstart: function(inEvent) {
      this.setPrimaryTouch(inEvent.changedTouches[0]);
      if (!this.scrolling) {
         inEvent.preventDefault();
         this.processTouches(inEvent, this.overDown);
      }
    },

This is similar to what is being done in touchmove().

Use getElementsFromPoint for enter/leave detection

The only good way to track enter/leave to spec is to track each and every element under the cursor on over/out (or just all the time for touch).

Only a proposal in www-style and public-webapps, but getElementsFromPoint will give list of elements under the cursor in paint order.

Polyfillable with steps in proposal.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.