AngularUI Calendar (FullCalendar) dayClick not work expectedly on iOS

bowerで入れた angularUI Calendar 1.0.0, FullCalendar 2.1.1での話です。

Angular WEBアプリでカレンダーを使いたいというニーズを完璧なまでに満たす AngularUI Calendar http://angular-ui.github.io/ui-calendar/

ですが、月(month)表示から日セルクリックで日(agendaDay)表示に切り替えようとしてdayClickを使用した場合、なぜかiPhoneではうまく動作しません。

正確にいえば、タップではなく一秒ほど長押しするか、軽く日セルをドラッグしようとするとdayClickが呼ばれます。

なんでどぅあぁぁぁーーーー!

と3日ほど頭を悩ませた結果、なんとか対処できたのでおすそ分けします。 ちなみにAngularUI CalendarはFullCalendarのラッパーなので、FullCalendarで同じような問題に悩んでいる方にも効くはず….です。

原因

dragイベントの扱いの違いに起因しています。 dayClickはclickイベントで呼ばれていません。dragの終了を検知して呼ばれています。 iOS上のSafari(というかwebkit?)以外では、タップして指を離した瞬間に drag開始、drag終了のイベントが発生するのですが、iOSではdragするか、しばしタップし続けないとdrag終了イベントが発生しないためのようです。

どうにもならんのでタップイベントでdayClickを呼ぶようFullCalendar.jsを修正しました。

4038行目付近

    coordMap: null, // a GridCoordMap that converts pixel values to datetimes
    cellDuration: null, // a cell's duration. subclasses must assign this ASAP
    isTouch : false, // separate touch and scroll.

    // Renders the grid into the `el` element.

4093行目付近

        this.el.on('mousedown', function(ev) {
            if (
                !$(ev.target).is('.fc-event-container *, .fc-more') && // not an an event element, or "more.." link
                !$(ev.target).closest('.fc-popover').length // not on a popover (like the "more.." events one)
            ) {
                _this.dayMousedown(ev);
            }
        });

        this.el.on('touchstart', function (ev) {
            this.isTouch = true;
        });

        this.el.on('touchmove', function (ev) {
            this.isTouch = false;
        });

        this.el.on('touchend', function (ev) {
                if (this.isTouch == false) {
                    //may be scroll.
                    return;
                }
                if (
                    !$(ev.target).is('.fc-event-container *, .fc-more') && // not an an event element, or "more.." link
                    !$(ev.target).closest('.fc-popover').length // not on a popover (like the "more.." events one)
                ) {
                    _this.touchEnd(ev);
                }
            }
        );

        this.bindSegHandlers(); // attach event-element-related handlers. in Grid.events.js
    },

    touchEnd: function(ev){
        console.log("touch end called.");
        var _this = this;
        var view = this.view;

        this.coordMap.build();
        var cell = _this.coordMap.getCell(ev.originalEvent.pageX, ev.originalEvent.pageY);
        var dayEl = _this.getCellDayEl(cell);
        view.trigger('dayClick', dayEl[0], cell.date, ev);
    },

    // Process a mousedown on an element that represents a day. For day clicking and selecting.
    dayMousedown: function(ev) {

単にtouchendだけ捕まえるとドラックされた時に挙動がおかしくなる場合があるので、touchstart,touchmove も捕まえてドラックとタップを識別しています。

参考になれば!

変更したfullcalendar.js fullcalendar.js