React-dates: Mobile Date Picker Doesn't Open on first Click

Created on 11 Jan 2017  路  19Comments  路  Source: airbnb/react-dates

I'm experiencing an issue with various settings on the latest iOS with the portal and without the portal. In which a prompt on the device appears either via a Done or an X as shown here:

glass_and_iphone_6_ _ios_10_2__14c89_

The user has to click done in order for the prompt to pop up. This is only an issue on iOS devices and not on android phones.

      <SingleDatePicker
        {...this.props}
        id="date_input"
        date={date}
        numberOfMonths={months}
        placeholder="00/00/00"
        focused={focused}
        onDateChange={this.onDateChange}
        onFocusChange={this.onFocusChange}
      />
      <SingleDatePicker
        {...this.props}
        id="date_input"
        date={date}
        withFullScreenPortal={true}
        withPortal={true}
        numberOfMonths={months}
        placeholder="00/00/00"
        focused={focused}
        onDateChange={this.onDateChange}
        onFocusChange={this.onFocusChange}
      />
bug

All 19 comments

Huh. That's really weird. I haven't seen that before. This is in Safari on IOS 8? I will investigate.

@moonboots have you been testing on any iOS devices recently?

This is on Safari and Chrome it's specific to iOS handling of fields not just safari.

I haven't seen this issue before and unfortunately wasn't able to reproduce in the storybook examples on an iPhone. I think the bar with the "Done" button appears because DateInput contains a text field for keyboard input, but this shouldn't prevent the focus/appearance of the calendar. @iamkevingreen, could you provide the implementation of your onFocusChange callback? Does it interact with any other elements on the page? Thanks

Sure @moonboots , let me provide my entire implementation:

 updateStart(startDate) {
    this.setState({ startDate })
 }
render() {
   return (
       <SingleDatePickerWrapper startDatePlaceholderText="00/00/00" updateDate={this.updateStart} />
     )
}
class SingleDatePickerWrapper extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      focused: false,
      date: null,
      months: 1
    };

    this.onDateChange = this.onDateChange.bind(this)
    this.onFocusChange = this.onFocusChange.bind(this)
  }

  onDateChange(date) {
    this.setState({ date })
    this.props.updateDate(date)
  }

  onFocusChange({ focused }) {
    this.setState({ focused })
  }

  render() {
    const { focused, date, months } = this.state

    return (
      <SingleDatePicker
        {...this.props}
        id="date_input"
        date={date}
        numberOfMonths={months}
        placeholder="00/00/00"
        focused={focused}
        onDateChange={this.onDateChange}
        onFocusChange={this.onFocusChange}
      />
    );
  }
}

Should be fixed in v6.0.2! Thanks!

I'm still having this issue, I see it pop up but then it gets pushed behind the dom until I hit done and then I can see it and make a selection on 10.2.1, this is both the normal datepicker and withportal. withfullportal makes the screen white, then if I hit done it shows the picker.

I'm on 6.0.2 now but still see the same issue. works amazing on android still.

I'm reopening this so we can continue tracking this issue (since it is definitely still there).

Hi,

i found a solution.
If replace actual componentDidMount in OutsideClickHandler with this:

componentDidMount() {
    const ua = navigator.userAgent || navigator.vendor || window.opera;
    const event = (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) ? 'touchstart' : 'click';
    // `capture` flag is set to true so that a `stopPropagation` in the children
    // will not prevent all outside click handlers from firing - maja
    this.clickHandle = addEventListener(
      document,
      event,
      this.onOutsideClick,
      { capture: true },
    );
  }

It works correctly.
Tested in my iPhone 5 v10.2.1
Found solution here http://stackoverflow.com/a/9480339/4930382

We wouldn't want to merge in a user agent check; a feature detection check would be better.

I found this approach:

let placeholder = document.createElement('div');
let isSupportedTouch = 'ontouchend' in placeholder;
// cross-browser check
if (!isSupportedTouch) {
  placeholder.setAttribute('ontouchend', 'return;');
  isSupportedTouch = typeof placeholder.ontouchend === 'function';
}
const event = isSupportedTouch ? 'touchstart' : 'click';
placeholder = null;

What do you think?

@StefanoPastore Maybe there is a little error (oversight) in your code:
const event = isSupportedTouch ? 'touchstart' : 'click'; would be const event = isSupportedTouch ? 'touchend' : 'click'; ?
With touchend event.

@timhecker thanks. I'm sorry for my mistake, yesterday it was very later ;)

Tried a fork with this fix, but no change. Is this what you had in mind?

componentDidMount() {
    let placeholder = document.createElement('div');
    let isSupportedTouch = 'ontouchend' in placeholder;
    // cross-browser check
    if (!isSupportedTouch) {
      placeholder.setAttribute('ontouchend', 'return;');
      isSupportedTouch = typeof placeholder.ontouchend === 'function';
    }
    const event = isSupportedTouch ? 'ontouchend' : 'click';
    placeholder = null;
    this.clickHandle = addEventListener(
      document,
      event,
      this.onOutsideClick,
      { capture: true },
    );
  }

@NickTiut const event = isSupportedTouch ? 'touchend' : 'click'; not ontouchend :)

Oh, missed that. I tried again, but still no change.

@NickTiut My code fixes the overlay closing in withPortal mode on the latest iOS version.

EDIT

For the best practice I think is preferable use utils/IsTouchDevice to check, so my last code is:

import React, { PropTypes } from 'react';
import { addEventListener, removeEventListener } from 'consolidated-events';
import isTouchDevice from '../utils/isTouchDevice';

...

constructor(props) {
    super(props);
    this.onOutsideClick = this.onOutsideClick.bind(this);
    this.isTouchDevice = isTouchDevice();
}

componentDidMount() {
    const event = this.isTouchDevice ? 'touchend' : 'click';

    // `capture` flag is set to true so that a `stopPropagation` in the children
    // will not prevent all outside click handlers from firing - maja
    this.clickHandle = addEventListener(
      document,
      event,
      this.onOutsideClick,
      { capture: true },
    );
}

...

The overlay closing works fine with that fix - iOS 9.
For the original issue (Mobile Date Picker Doesn't Open on first Click) possible solution for mobile: to set the actual input as disabled, and handle the opening of the datepicker on a higher level component?

Any news on this? Anything I can do to help?
An ugly workaround I'm using for this is to blur the input on focus:
onFocusChange={(focused) => { $('.DateInput__input').blur(); self.setState({ focusedInput: focused }); }}
It flashes the done bar on the bottom for a sec, and then the datepicker appears.

This should be fixed in #410. Sorry for how hella delayed it was!

Was this page helpful?
0 / 5 - 0 ratings