ライブラリいじってカレンダーをいい感じにした話

この記事は、[株式会社Y's アドベントカレンダー](https://qiita.com/advent-calendar/2017/ysinc)23日目の記事になります。

##はじめに
これはJqueryとJquery UIのみ使用可の案件に途中から入り、カレンダーをいい感じにしたときのお話である。
ちなみに今までにJquery UIは使ったことはないです。

##そもそもjqueryUIとかdatepickerって？
ライブラリです。機能がいっぱいあります。
datepickerはその中のカレンダー機能みたいな感じです。ふわふわした説明ですみません…
##まずは現状
jqueryUIのdatepickerを使用してカレンダーが作られている

<img width="664" alt="cal-02.png" src="https://qiita-image-store.s3.amazonaws.com/0/225308/17a5d696-4105-a6ca-5741-16b8363b5b12.png">
CSSあんまり書きたくないので今回はbootstrap3使って見た目それっぽくしてます。
datepickerの設置の仕方とかjqueryUIの使い方については割愛します。

##やりたいこと
某旅行会社とかみたいに、選択期間が塗りつぶされるようにしたい

![rakuten.png](https://qiita-image-store.s3.amazonaws.com/0/225308/84631f5c-ad3f-2371-25c2-f311b02e15cf.png)


期間が選択されていると塗りつぶされる

## どう対応したか
まず塗りつぶす用のスタイルを作成

```css:style.css
#ui-datepicker-div .ui-state-select {
  border: 1px solid #003eff;
  background: #87bdf3;
  font-weight: normal;
  color: #fff;
}
```
次はそのクラスをカレンダーに当てる
focusイベントで上記のクラス付与してみる
特に条件指定してないんで普通に付与できればすべての日付の部分が水色で染まるはず

```javascript:calendar.js
$(document).on('focus', 'input', function () {
    $('.ui-state-default').addClass('ui-state-select');
});

```
<img width="865" alt="cal-03.png" src="https://qiita-image-store.s3.amazonaws.com/0/225308/d5f0cdd8-65f4-c65a-81c8-d01c3e41cc6f.png">

イベント発生時にDOMが生成されていないので当たらない
じゃあDelayさせてみる

```javascript:calendar.js
$(document).on('focus', 'input', function () {
    setTimeout(function () {
        $('.ui-state-default').addClass('ui-state-select');
    }, 100);
});
```
<img width="969" alt="cal-03-02.png" src="https://qiita-image-store.s3.amazonaws.com/0/225308/b034f49a-2e49-4a23-d77c-2224794ec76b.png">

当たるっちゃ当たる
けど生成とイベント発生に時間差があるので表示時に微妙にチラついて気持ち悪い

じゃあDOM生成と同じタイミングでクラス当てればいいのでは？？？
ということで
### ライブラリいじりました

```javascript:jquery-ui.js
var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
    controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
    monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
    selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
    cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
    printDate, dRow, tbody, daySettings, otherMonth, unselectable,
    tempDate = new Date(),
    today = this._daylightSavingAdjust(
        new Date(tempDate.getFullYear(), 　　　　　　tempDate.getMonth(),tempDate.getDate())), // clear time
    isRTL = this._get(inst, "isRTL"),
    showButtonPanel = this._get(inst, "showButtonPanel"),
    hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
    navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
    numMonths = this._getNumberOfMonths(inst),
    showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
    stepMonths = this._get(inst, "stepMonths"),
    isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
    currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
        new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
    minDate = this._getMinMaxDate(inst, "min"),
    maxDate = this._getMinMaxDate(inst, "max"),
    drawMonth = inst.drawMonth - showCurrentAtPos,
    drawYear = inst.drawYear;


    /* user change */
    var startDate = $('#start-date').val(),
        endDate = $('#end-date').val(),
        startSel = false;

    if (startDate && endDate) {
        startSel = true;
        var startDate = new Date(startDate),
            endDate = new Date(endDate);
    }
```
 /* user change */以降が追加した処理
datepickerで使用する変数沢山宣言してるところに追加で
__開始日と終了日がどちらも入力されているとフラグtrue
開始日と終了日のDateインスタンスを作成__

```javascript:jquery-ui.js
for (dow = 0; dow < 7; dow++) { // create date picker days
    daySettings = (beforeShowDay ?
        beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
    otherMonth = (printDate.getMonth() !== drawMonth);
    unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
        (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
    tbody += "<td class='" +
        ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
        (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
        ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
            (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
            // or defaultDate is current printedDate and defaultDate is selectedDate
            " " + this._dayOverClass : "") + // highlight selected day
        (unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "") + // highlight unselectable days
        (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
            (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
            (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
        ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "&#39;") + "'" : "") + // cell title
        (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
        (otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
            (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
                (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
                (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day

                /* user change */
                (startSel && startDate.getTime() <= printDate.getTime() && printDate.getTime() <= endDate.getTime() ? " ui-state-select" : "") +

                (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
                "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
    printDate.setDate(printDate.getDate() + 1);
    printDate = this._daylightSavingAdjust(printDate);
}
```

カレンダーのを生成している部分に
（// create date picker daysって書いてあるので分かりやすいですね）
__先程のフラグがtrueであれば開始日〜終了日にクラス付与__
という処理を記載

##結果

<img width="640" alt="cal-04.png" src="https://qiita-image-store.s3.amazonaws.com/0/225308/be9baba6-766c-b7b2-1745-5533f0479496.png">

できたー
カレンダーを作成しながらクラスを付与しているので時間の差分によるチラつきとかもない

##おわりに
あとでJquery UIのリファレンスをじっくり読んだ結果、
__beforeShowDay__というDOMの生成前にパラメータを渡せる素晴らしい関数が用意されていました （ライブラリいじる必要なかった…！）

ただ、ライブラリの中身を読んで内容をなんとなく理解して処理を足すというのは個人的に新鮮で、今後どこかで活かせるのではと思いました。

明日は@Larry_rahaiさんの記事です！乞うご期待！！！