最新消息:20210917 已从crifan.com换到crifan.org

【部分解决】ReactJS中react-mobile-datepicker中input被设置readonly时无法滚动选择日期

ReactJS crifan 6681浏览 0评论

之前用:

【已解决】ReactJS中如何整合已有的datetime选择框

已经可以利用:

lanjingling0510/react-mobile-datepicker: ? ? look a demo, Please imitate mobile environment.

实现基本的日期选择了。

但是之前有个问题就是:

之前的代码:

  render () {
    return (
      <input
        value={datetimeToStr(this.state.curDate, "请选择")}
        onClick={this.handleDateClick}
        class={style.ui_input}>
        <DatePicker
          value={this.state.curDate}
          isOpen={this.state.isOpen}
          theme={‘ios’}
          min={new Date(2010, 1, 1)}
          max={new Date(2050, 1, 1)}
          dateFormat={[‘YYYY年’, ‘MM月’, ‘DD日’]}
          onSelect={this.handleDateSelect}
          onCancel={this.handleDateCancel} />
      </input>
    );
  }

即:picker属于input,而input允许输入,所以此时点击选择日期时,会同时出发输入法且可以输入:

然后就去给input加上了

readonly=“readonly"

结果就导致了:

iOS和android都的确不会有输入框了

且iOS端仍然可以很流畅的滚动选择日期

但是:android端,基本上没发滚动选择日期了

极其偶尔的才可以滚动一下 但是马上又没发滚动了。

以为是android的bug

后来搜:

react-mobile-datepicker input readonly can not select

参考:

Disable manual entry of date in <input>, force selection via picker. · Issue #212 · Eonasdan/bootstrap-datetimepicker

javascript – Bootstrap datetimepicker don’t work with readonly or disabled – Stack Overflow

感觉是:

datetimepicker

这类控件,估计都是会受readonly的影响。。

现在先去试试,把input改为其他的,比如p

看看效果

实在不行,再去试试

readonly=“true”

readonly=“readonly”

readonly

disabled=“disabled"

disabled

之类的。

可以弹出日期选择:

但是value的属性就不起效果了。

想去找找其他input类似的控件

html 输入 控件

HTML <input> 标签的 disabled 属性

不用去试试disabled了

-》的确是其本意就是禁止

“被禁用的 input 元素既不可用,也不可点击。”

所以此处是不合适用disabled

HTML <input> 标签的 readonly 属性

适合用:readonly

HTML常用控件元素的用法 | Hom

试了试:

readonly=“true”

结果问题依旧,基本上没发滑动选择日期了。

试试:

readonly

问题依旧。

react datepicker input readonly can not select

react datepicker input not focus

Date picker still appears and date can be selected/changed when readOnly is true. · Issue #911 · Hacker0x01/react-datepicker

待会试试:

(ReactJS中的)readOnly

问题依旧。

或者待会试试:

jquery – How to make bootstrap datepicker readonly? – Stack Overflow

中onClick时,禁止传递event

加上:

    e.stopPropagation();

    e.nativeEvent.stopImmediatePropagation();

即代码:

  handleDateClick(e) {
    console.log("handleDateClick");
    console.log(`this.state.curDate=${this.state.curDate}`);
    console.log(`this.state.prevSelectedDate=${this.state.prevSelectedDate}`);
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
    if (this.state.curDate === INVALID_DATE) {
      this.setState({
        curDate: new Date(),
        prevSelectedDate : INVALID_DATE
      });
    }
    this.setState({ isOpen : true });
  }
  render () {
    return (
      <input
        value={datetimeToStr(this.state.curDate, "请选择")}
        onClick={this.handleDateClick}
        readOnly
        class={style.ui_input}>
        <DatePicker
          value={this.state.curDate}
          isOpen={this.state.isOpen}
          theme={‘ios’}
          min={new Date(2010, 1, 1)}
          max={new Date(2050, 1, 1)}
          dateFormat={[‘YYYY年’, ‘MM月’, ‘DD日’]}
          onSelect={this.handleDateSelect}
          onCancel={this.handleDateCancel} />
      </input>
    );
  }

看看效果:

问题和以前一样了:会出现输入框,并且可以输入了。。。

react-datepicker interferes with react select · Issue #730 · Hacker0x01/react-datepicker

以后可以考虑看看这个cancelFocusInput

react datepicker input readonly

react mobile datepicker input readonly

make disable or readonly input field · Issue #961 · Hacker0x01/react-datepicker

Blinking cursor in mobile · Issue #253 · airbnb/react-dates

To don’t allow DatePicker keyboard input – Date/Time Pickers – Kendo UI Forum

试试:

disabled=“disabled"

不用去手机端试了,Chrome中就无法点击了。。

需要去搞清楚:

readonly="true"和readonly=“readonly"的区别:

input readonly="true" readonly=“readonly"

html – What is the difference between readonly="true" & readonly="readonly"? – Stack Overflow

html – What is the correct readonly attribute syntax for input text elements? – Stack Overflow

readonly只要出现,而不论其是否有值,值是readonly还是true(还是false)

都是一样的效果:表示是只读。

readonly只要不出现,则表示可以输入。

Blinking cursor in mobile · Issue #253 · airbnb/react-dates

目前感觉是:

设置input为readonly了,android端有问题,无法滚动了

像是android中的chrome内核有bug?

现在的想法只有:

(1)看看是否能想办法规避这个bug

(2)是否可以禁止显示输入框?

android chrome input readonly datepicker

asp.net mvc – How to not show a mobile keyboard when using bootstrap-datepicker? – Stack Overflow

ignoreReadonly这只是Bootstrap的,此处没法用。

How to prevent mobile keyboard to show when opening the picker? · Issue #1668 · Eonasdan/bootstrap-datetimepicker

不过还是先去试试:

    e.preventDefault();
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();

问题依旧。

【已解决】ReactJS的H5页面中如何去掉输入框获得焦点时出现的黄色边框

试试:

type=“button”

      <input
        value={datetimeToStr(this.state.curDate, "请选择")}
        onClick={this.handleDateClick}
        type="button"
        class={style.ui_input}>
        <DatePicker
          value={this.state.curDate}
          isOpen={this.state.isOpen}
          theme={‘ios’}
          min={new Date(2010, 1, 1)}
          max={new Date(2050, 1, 1)}
          dateFormat={[‘YYYY年’, ‘MM月’, ‘DD日’]}
          onSelect={this.handleDateSelect}
          onCancel={this.handleDateCancel} />
      </input>

Mac中Chrome的效果还是可以的。

然后再去把width从100%改为auto,就好看多了:

再去试试移动端

结果是:

Android端还是有问题:

虽然无法输入了,还是很难滑动选择日期,而iOS端WKWebView就很流畅。

现在感觉可能是之前的另外的问题导致的Android端滑动有问题:

【暂未解决】ReactJS代码运行出现警告:[Violation] Added non-passive event listener to a scroll-blocking touchstart event

【已解决】ReactJS中如何禁止input控件获得输入的焦点以避免弹出键盘和输入

此处虽然可以禁止输入了,但是滑动滚动选择日期,又基本失效了。。。

所以算是问题依旧。

去看了看其源码,感觉主要是:

    componentDidMount() {
        const viewport = this.viewport;
        viewport.addEventListener(‘touchstart’, this.handleContentTouch, false);
        viewport.addEventListener(‘touchmove’, this.handleContentTouch, false);
        viewport.addEventListener(‘touchend’, this.handleContentTouch, false);
        viewport.addEventListener(‘mousedown’, this.handleContentMouseDown, false);
    }

去注册了滑动事件

其中关于capture参数,参考:

EventTarget.addEventListener() – Web API 接口 | MDN

addEventListener的第三個參數 : O3noBLOG

HTML DOM addEventListener() 方法 | 菜鸟教程

然后用:

    /**
     * 滑动日期选择器触屏事件
     * @param  {Object} event 事件对象
     * @return {undefined}
     */
    handleContentTouch(event) {
        event.preventDefault();
        if (this.animating) return;
        if (event.type === ‘touchstart’) {
            this.handleStart(event);
        } else if (event.type === ‘touchmove’) {
            this.handleMove(event);
        } else if (event.type === ‘touchend’) {
            this.handleEnd(event);
        }
    }

去检测触摸滚动

所以问题感觉是:

使用了preventDefault,或者是input无法获得输入焦点了,readonly了等等

scroll滚动时间,触摸事件,就失效了?

或者就极其不流畅了?

react js preventDefault scroll not work

react js preventDefault touchstart not work

reactjs – React JS: onWheel bubbling stopPropagation not working – Stack Overflow

javascript – How to disable scrolling temporarily? – Stack Overflow

onWheel Stop bubbling is not happening. · Issue #5845 · facebook/react

Bar scroll. The default ‘touchmove’ is not prevented on Mobile Safari · Issue #654 · twbs/ratchet

javascript – ReactJS SyntheticEvent stopPropagation() only works with React events? – Stack Overflow

html – preventDefault() for touchstart handler in Firefox not working – Pure Javascript – Stack Overflow

touchstart preventDefault() does not prevent click event. · Issue #9809 · facebook/react

onTouchTap’s event.preventDefault() not working for click events · Issue #7 · zilverline/react-tap-event-plugin

还是无解。

参考实例代码,去给DatePicker上一级加上一个div试试

      <input
        value={datetimeToStr(this.state.curDate, "请选择")}
        onClick={this.handleDateClick}
        class={style.ui_input}>
        <div>
          <DatePicker
            value={this.state.curDate}
            isOpen={this.state.isOpen}
            ignoreReadonly={true}
            theme={‘ios’}
            min={new Date(2010, 1, 1)}
            max={new Date(2050, 1, 1)}
            dateFormat={[‘YYYY年’, ‘MM月’, ‘DD日’]}
            onSelect={this.handleDateSelect}
            onCancel={this.handleDateCancel} />
        </div>
      </input>

结果问题依旧:

还是滚动基本上无效,或者反应很迟钝。

去把react-mobile-datepicker的源码中的

    /**
     * 滑动日期选择器触屏事件
     * @param  {Object} event 事件对象
     * @return {undefined}
     */
    handleContentTouch(event) {
        // event.preventDefault();
        if (this.animating) return;
        if (event.type === ‘touchstart’) {
            this.handleStart(event);
        } else if (event.type === ‘touchmove’) {
            this.handleMove(event);
        } else if (event.type === ‘touchend’) {
            this.handleEnd(event);
        }
    }

// event.preventDefault();

看看效果。

问题依旧。

react js   touchstart not work

[FlatButton] – onTouchTap event works instead of onTouchStart · Issue #4555 · callemall/material-ui

Touch events not firing:reactjs

看到提示说是CSS中要有:

cursor: pointer;

所以去看库的源码,倒是都有:

            <div className="App">
                <a
                    className="select-btn"
                    onClick={this.handleClick}>
                    select time
                </a>
                <DatePicker
                    value={this.state.time}
                    isOpen={this.state.isOpen}
                    onSelect={this.handleSelect}
                    onCancel={this.handleCancel} />
            </div>
.select-btn {
    display: inline-block;
    border: 1px solid #ccc;
    padding: 1rem;
    margin: 1rem 0;
    cursor: pointer;
    background: #fff;
    &.sm {
        margin: .5rem .5rem;
        padding: .5rem;
    }
}

 

所以自己此处也去加上试试

CSS cursor 属性

CSS cursor 属性 – 鼠标效果 – CSS教程

不用去试了,只是鼠标的形状而已。。。

难道是库中代码的refs的写法 

(之前解决过:

【已解决】[eslint] Using this.refs is deprecated. (react/no-string-refs)

导致此处滑动时不时不生效的问题?

Refs and the DOM – React

Update nuances of ref callback calling by unel · Pull Request #8333 · facebook/react

那抽空把代码换成新的写法试试

但是不方便直接修改库的源码

所以去尝试把库的源码拷贝到项目中,然后去调试

然后出错:

【未解决】Preact中css代码出错:Error evaluating function `color`: keyword.toLowerCase is not a function

算了,放弃整合react-mobile-datepicker代码到此preact项目中了。

去加了调试代码,也没看出什么问题。

去把refs改为正常的引用:

/node_modules/react-mobile-datepicker/dist/react-mobile-datepicker.js

                            // ref: ‘scroll’,
                            ref: function (scroll) {
                                return _this3.scroll = scroll;
                            }, // eslint-disable-line
                            className: ‘datepicker-scroll’,
    DatePickerItem.prototype._moveTo = function _moveTo(obj, currentIndex) {
            //_this2._clearTransition(_this2.refs.scroll);
            _this2._clearTransition(_this2.scroll);
    DatePickerItem.prototype._moveToNext = function _moveToNext(direction) {
        //this._moveTo(this.refs.scroll, this.currentIndex);
        this._moveTo(this.scroll, this.currentIndex);

感觉是:

或许是此处的refs有问题,导致了:

_moveTo

_moveToNext

中的滚动没有起到效果,或者是很难起效果。

但是直接修改了:

react-mobile-datepicker.js

导致一个问题:

【已解决】npm run build项目出错:npm ERR! code ELIFECYCLE errno 2

所以还是:

最好从项目react-mobile-datepicker的源码,修改后,自己去build出来对应的 dist中的.js和.min.js

所以去:

【已解决】如何从项目react-mobile-datepicker的js源码编译并发布js库

然后用修复了refs的库,结果android端还是问题依旧:

有时候是好的,可以正常滑动滚动选择日期,虽然不是特别流畅,但是大部分时间都是无法滚动的。

-》所以确定不是refs的问题,而是其他什么原因导致滚动问题的。

然后也去对比了:

类似的代码和逻辑的:

处理页面的滚动,就基本上是正常工作的(虽然也不是极度的流畅)

而此处的日期选择的滚动就基本不工作。。

继续研究代码:

【已解决】尝试看看是否是Android浏览器对transform和translateY支持不够好导致无法滚动选择日期

所以,问题方向要调整到,找到:

加了readonly,或者允许输入法弹出后,为何android端就可以正常滑动选择了。

android js input popup then datepicker work

突然想起来,把input和DatePicker分开,不要是父子,而是sibling:

      <div>
        <input
          value={datetimeToStr(this.state.curDate, "请选择")}
          readonly
          onClick={this.handleDateClick}
          class={style.ui_input} />
        <DatePicker
          value={this.state.curDate}
          isOpen={this.state.isOpen}
          ignoreReadonly={true}
          theme={‘default’}
          min={new Date(2010, 1, 1)}
          max={new Date(2050, 1, 1)}
          dateFormat={[‘YYYY年’, ‘MM月’, ‘DD日’]}
          onSelect={this.handleDateSelect}
          onCancel={this.handleDateCancel} />
      </div>

看看效果。

果然问题依旧。

不过突然想起来:

难道是Android端的日期选择控件,只有是input获取焦点之后,才能开始滑动???

android datepicker only scroll after get focus

android datepicker only scroll after input focus

jquery datetime picker problem. | Laravel.io

Input click breaks scrolling · Issue #1638 · ionic-team/ionic

现象和我这有点类似

fix(keyboard):don’t setKeyboardShow on date/time inputs · ionic-team/ionic@ad08b34

css – Input field overlap by keyboard and scrolls up after first character entered in Android using Cordova 6.3.1 – Stack Overflow

iOS 8.x modal scroll issue · Issue #14839 · twbs/bootstrap

jquery – Disable scrolling when changing focus form elements ipad web app – Stack Overflow

webchromeclient scroll not work not get focus

javascript – How to bring focused field into the view using iscroll and Android WebView – Stack Overflow

android – WebView doesn’t scroll when keyboard opened – Stack Overflow

java – Android webview scrolling doesn’t work – Stack Overflow

Android Froyo WebView doesn’t scroll vertically – Stack Overflow

android webview scroll not work if not get focus

Annoying iOS Safari input issues with workarounds | Mobiscroll Blog | Design, UI and UX for Successful Products

然后去试了试:

使用:

        <input
          value={datetimeToStr(this.state.curDate, "请选择")}
          type="date"
          onClick={this.handleDateClick}
          class={style.ui_input} />

原生的日期选择,其实倒是还是不错的:

只不过会和现在此处的日期选择器重叠:

Android端的效果:

iOS端的效果:

android webview input get focus

Why is Android WebView refusing user input? – Stack Overflow

Android WebView – Setting HTML Field focus using Javascript – Stack Overflow

WebView with input/textarea never get virtual keyboard focus [36915710] – Visible to Public – Issue Tracker

感觉可能是:

onTouchStart

onTouchMove

onTouchEnd

的事件,对于Android的webview中,当没有获得focus时,就不太起效果了?

去把:

            <div className="datepicker-col-1">
                <div
                    ref={viewport => this.viewport = viewport} // eslint-disable-line
    componentDidMount() {
        const viewport = this.viewport;
        viewport.addEventListener(‘touchstart’, this.handleContentTouch, false);
        viewport.addEventListener(‘touchmove’, this.handleContentTouch, false);
        viewport.addEventListener(‘touchend’, this.handleContentTouch, false);
        viewport.addEventListener(‘mousedown’, this.handleContentMouseDown, false);
    }
    componentWillUnmount() {
        // alert(`DatePickerItem componentWillUnmount`);
        const viewport = this.viewport;
        viewport.removeEventListener(‘touchstart’, this.handleContentTouch, false);
        viewport.removeEventListener(‘touchmove’, this.handleContentTouch, false);
        viewport.removeEventListener(‘touchend’, this.handleContentTouch, false);
        viewport.removeEventListener(‘mousedown’, this.handleContentMouseDown, false);
    }

换成ReactJS的:

onTouchStart

onTouchMove

onTouchEnd

            <div className="datepicker-col-1">
                <div
                    onTouchStart={this.handleContentTouch}
                    onTouchMove={this.handleContentTouch}
                    onTouchEnd={this.handleContentTouch}
                    onMouseDown={this.handleContentMouseDown}

结果发现:

    /**
     * 滑动日期选择器触屏事件
     * @param  {Object} event 事件对象
     * @return {undefined}
     */
    handleContentTouch(event) {
        event.preventDefault();
        if (this.animating) return;
        if (event.type === ‘touchstart’) {
            this.handleStart(event);
        } else if (event.type === ‘touchmove’) {
            this.handleMove(event);
        } else if (event.type === ‘touchend’) {
            this.handleEnd(event);
        }
    }

也要去改掉,因为React的SyntheticEvent没有type

另外先去:

android webview javascript touchstart not work

touchstart and touchend events does not work on Webview in Android – Stack Overflow

javascript – touchend event doesn’t work on Android – Stack Overflow

javascript – Android browsers not handling touchmove events correctly – Stack Overflow

javascript – Touchstart event never be triggered on Android Chrome at the first time of page loading – Stack Overflow

Android – JavaScript : touchstart event not fired until zoom or scroll the page – Stack Overflow

android webview react js touchstart not work

android webview react js touchstart sometime not work

后来的后来,

不去继续测试自己的Android手机:锤子M1L了 Android 6.0.1

换用:

别的安卓手机:小米6 Android 7.1.1系统,然后日期选择的滚动是很完美的运行的。

另外再去换用:

直接用自己的锤子里面的移动端浏览器去打开对应的页面:

锥子内置浏览器

QQ浏览器

都是可以正常滑动选择日期的:

【总结】

经过一堆的测试:

别的安卓手机(小米6 Android 7.1.1、小米4C Android 7.0),和别的移动端浏览器(QQ浏览器,锥子自带浏览器),都是正常可以滑动选择日期的。

就是我自己的锥子的M1L的Android 6.0.1 中,无法正常滑动选择日期。

算是我的手机的适配性有问题。不算是程序的bug。

【后记1】

然后让Android人员帮忙打包了一个,用WebViewClient(之前是用WebChromeClient)的浏览器内核,然后日期滑动选择问题,貌似有一点点改善?但是问题依旧。

【后记2】

【未解决】锤子M1L浏览器内核滑动选择日期时出现警告:chromium Ignored attempt to cancel a touchend event

但是,对于此处的react-mobile-datepicker库来说,其代码写的也是有待改进的,此处,专门去记录,改动后的代码,以备后查:

【记录】修改后的react-mobile-datepicker的源码

【整理】Android中的两种Webview:WebViewClient WebChromeClient

【整理】Android中的Webview的内核

【整理】Android手机端浏览器内核:Crosswalk vs 腾讯X5

然后再去想办法,连接手机端的webview去调试:

【已解决】如何远程调试Android手机端的原生app内嵌的Chrome内核的浏览器的H5页面

结果chrome有bug,没法远程调试->后来发现是:手机特殊,没法调试,换个android就可以chrome调试了。

再去把:

event.preventDefault();

换成:

            event.stopPropagation();

            event.nativeEvent.stopImmediatePropagation();

试试:

    /**
     * 滑动日期选择器触屏事件
     * @param  {Object} event 事件对象
     * @return {undefined}
     */
    handleContentTouch(event) {
        // event.preventDefault();
        if (this.animating) return;
        if (event.type === ‘touchstart’) {
            // event.preventDefault();
            event.stopPropagation();
            event.nativeEvent.stopImmediatePropagation();
            this.handleStart(event);
        } else if (event.type === ‘touchmove’) {
            this.handleMove(event);
        } else if (event.type === ‘touchend’) {
            // event.preventDefault();
            event.stopPropagation();
            event.nativeEvent.stopImmediatePropagation();
            this.handleEnd(event);
        }
    }

结果:

还是没有更多的改进。

【总结】

此处,对于现象:

锥子M1L的(Android 6.0.1)手机中,app内嵌Webview中显示年月日的滚动条,基本上没发工作,用力滑动很多次,基本上很难滚动,偶尔巧了,能滚动几下,但是马上又无法滚动了。

最终的结论是:

主要还是自己的手机的哪里不兼容的问题,导致上述日期滑动的问题的。

初步判断,或许是Android手机内置的webview的版本太低?但是目前没发验证,不知道内嵌webview的版本是多少

而别的手机:

(1)小米4C Android 7.0

(2)小米6 Android 7.1.1

(3)锥子M1L的手机端其他浏览器:QQ浏览器,锥子内置浏览器

都是可以正常混动选择日期的。

且同样代码,iOS的iPhone端,始终都是可以顺畅的滚动的。

经过一番调试和优化,最终:

通过对于:

/node_modules/react-mobile-datepicker/lib/DatePickerItem.js

把:

    handleContentTouch(event) {
        event.preventDefault();
        if (this.animating) return;
        if (event.type === ‘touchstart’) {
            this.handleStart(event);
        } else if (event.type === ‘touchmove’) {
            this.handleMove(event);
        } else if (event.type === ‘touchend’) {
            this.handleEnd(event);
        }
    }

改为:

    handleContentTouch(event) {
        if (this.animating) return;
        if (event.type === ‘touchstart’) {
            event.preventDefault();
            this.handleStart(event);
        } else if (event.type === ‘touchmove’) {
            this.handleMove(event);
        } else if (event.type === ‘touchend’) {
            event.preventDefault();
            this.handleEnd(event);
        }
    }

把锥子M1L中的日期滑动,从90%的不工作,优化到50%的不工作(开始几次用力滑动后,之后基本上就可以顺畅滑动了)

但是还是没有彻底解决问题,也没有找到根本原因。以后再说。

备注:

react-component/m-date-picker: React Mobile DatePicker(web & react-native)

看起来好像还可以,以后抽空可以去试试效果如何。

转载请注明:在路上 » 【部分解决】ReactJS中react-mobile-datepicker中input被设置readonly时无法滚动选择日期

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)

  1. 找到原因了吗 ~
    rainie7年前 (2018-04-24)回复
92 queries in 0.193 seconds, using 20.21MB memory