Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Actually prevent passive touchmove events default behavior #88

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ as well as the x distance, + or -, from where the swipe started to where it ende

**`delta`** is the amount of px before we start firing events. Also affects how far `onSwipedUp`, `onSwipedRight`, `onSwipedDown`, and `onSwipedLeft` need to be before they fire events. The default value is `10`.

**`preventDefaultTouchmoveEvent`** is whether to prevent the browser's [touchmove](https://developer.mozilla.org/en-US/docs/Web/Events/touchmove) event. Sometimes you would like the target to scroll natively. The default value is `false`. [Chrome 56 and later, warning with preventDefault](#chrome-56-and-later-warning-with-preventdefault)
**`preventDefaultTouchmoveEvent`** is whether to prevent the browser's [touchmove](https://developer.mozilla.org/en-US/docs/Web/Events/touchmove) event. Sometimes you would like the target to scroll natively. The default value is `false`.
* **Notes** `e.preventDefault()` is only called when `preventDefaultTouchmoveEvent` is `true` **and** the user is swiping in a direction that has an associated directional `onSwiping` or `onSwiped` prop.
* Example: user is swiping right with `<Swipable onSwipedRight={this.userSwipedRight} preventDefaultTouchmoveEvent={true} >` then `e.preventDefault()` will be called, but if user was swiping left `e.preventDefault()` would **not** be called.
* Please experiment with [example](http://dogfessional.github.io/react-swipeable/) to test `preventDefaultTouchmoveEvent`.
Expand Down Expand Up @@ -112,20 +112,6 @@ as well as the x distance, + or -, from where the swipe started to where it ende
innerRef: PropTypes.func,
```

### Chrome 56 and later, warning with preventDefault
When this library tries to call `e.preventDefault()` in Chrome 56+ a warning is logged:
`Unable to preventDefault inside passive event listener due to target being treated as passive.`

This warning is because this [change](https://developers.google.com/web/updates/2017/01/scrolling-intervention) to Chrome 56+ and the way the synthetic events are setup in reactjs.

If you'd like to prevent all scrolling/zooming within a `<Swipeable />` component you can pass a `touchAction` style property equal to `'none'`, [example](https://github.com/dogfessional/react-swipeable/blob/master/examples/app/Main.js#L143). Chrome's recommendation for [reference](https://developers.google.com/web/updates/2017/01/scrolling-intervention).

```
<Swipeable style={{touchAction: 'none'}} />
```

Follow reacts handling of this issue here: [facebook/react#8968](https://github.com/facebook/react/issues/8968)

## Development

Initial set up, run `npm install`.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"rimraf": "^2.6.1"
},
"dependencies": {
"detect-passive-events": "^1.0.4",
"prop-types": "^15.5.8"
},
"peerDependencies": {
Expand Down
58 changes: 52 additions & 6 deletions src/Swipeable.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* global document */
const React = require('react');
const PropTypes = require('prop-types');
const DetectPassiveEvents = require('detect-passive-events').default;

function getInitialState() {
return {
Expand Down Expand Up @@ -50,31 +51,66 @@ class Swipeable extends React.Component {
this.mouseUp = this.mouseUp.bind(this);
this.cleanupMouseListeners = this.cleanupMouseListeners.bind(this);
this.setupMouseListeners = this.setupMouseListeners.bind(this);
this.elementRef = this.elementRef.bind(this);
this.setupTouchmoveEvent = this.setupTouchmoveEvent.bind(this);
this.cleanupTouchmoveEvent = this.cleanupTouchmoveEvent.bind(this);
this.hasPassiveSupport = DetectPassiveEvents.hasSupport;
}

componentWillMount() {
// setup internal swipeable state
this.swipeable = getInitialState();
}

componentDidMount() {
// if we care about calling preventDefault and we have support for passive events
// we need to setup a custom event listener, sigh...
// there are a few jump ropes within the code for this case,
// but it is the best we can do to allow preventDefault for chrome 56+
if (this.props.preventDefaultTouchmoveEvent) {
this.setupTouchmoveEvent();
}
}

componentDidUpdate(prevProps) {
// swipeable toggled either on/off, so stop tracking swipes and clean up
if (prevProps.disabled !== this.props.disabled) {
this.cleanupMouseListeners();
// reset internal swipeable state
this.swipeable = getInitialState();
}

// preventDefaultTouchmoveEvent toggled off - clean up touch move if needed
if (prevProps.preventDefaultTouchmoveEvent && !this.props.preventDefaultTouchmoveEvent) {
this.cleanupTouchmoveEvent();

// preventDefaultTouchmoveEvent toggled on - add touch move if needed
} else if (!prevProps.preventDefaultTouchmoveEvent && this.props.preventDefaultTouchmoveEvent) {
this.setupTouchmoveEvent();
}
}

componentWillUnmount() {
this.cleanupMouseListeners();
}

setupTouchmoveEvent() {
if (this.element && this.hasPassiveSupport) {
this.element.addEventListener('touchmove', this.eventMove, { passive: false });
}
}

setupMouseListeners() {
document.addEventListener('mousemove', this.mouseMove);
document.addEventListener('mouseup', this.mouseUp);
}

cleanupTouchmoveEvent() {
if (this.element && this.hasPassiveSupport) {
this.element.removeEventListener('touchmove', this.eventMove, { passive: false });
}
}

cleanupMouseListeners() {
// safe to call, if no match is found has no effect
document.removeEventListener('mousemove', this.mouseMove);
Expand Down Expand Up @@ -208,18 +244,28 @@ class Swipeable extends React.Component {
this.swipeable = getInitialState();
}

elementRef(element) {
this.element = element;
this.props.innerRef && this.props.innerRef(element);
}

render() {
const { disabled, innerRef } = this.props;
const newProps = { ...this.props };
if (!disabled) {
if (!this.props.disabled) {
newProps.onTouchStart = this.eventStart;
newProps.onTouchMove = this.eventMove;

// if we do not care about calling preventDefault then assign onTouchMove prop
// else we need to also check for passive support
// and set a custom eventListener for touchmove on mount/update
if (!this.props.preventDefaultTouchmoveEvent || !this.hasPassiveSupport) {
newProps.onTouchMove = this.eventMove;
}

newProps.onTouchEnd = this.eventEnd;
newProps.onMouseDown = this.mouseDown;
}
if (innerRef) {
newProps.ref = innerRef;
}

newProps.ref = this.elementRef;

// clean up swipeable's props to avoid react warning
delete newProps.onSwiped;
Expand Down