JavaScript

こんにちは!
前回はJavaScriptでカレンダーを作成しました。
今回は、前回作成したカレンダーに祝日対応を組み込んでいきたいと思います!

前回の記事は下のリンクからどうぞ♪

はじめに

祝日の対応ですが、今回はCSVファイルを読み込んで日付判定を行います。
CSVファイルは内閣府のWebサイトから取得できます。
※ 文字コードをShift-JISからUTF-8に変更しました。

直接ダウンロードされる場合はここをクリックしてください。

データは日付、祝日名の2項目で作成されています。

フロー(箇条書き)

箇条書きで処理フローを記述していきます。
CSVファイルの読み込みと当月の日付判定のそれぞれで変更を加えます。

CSVファイル読み込み

  1. CSVファイルを読み込み
  2. 読み込み完了後、カレンダー表示

当月の日付判定

  1. その日付の情報を取得(本日か、祝日か等)
  2. 条件に応じてカレンダー(HTML)を設定

HTML

前回の記事から変更なしのため、こちらをご覧ください。

CSS

前回の記事とほとんど変わりなしですが、
祝日のクラス(=holiday)に日曜日と同様のcolorスタイルの設定を追加しました。

td:first-child,
td.holiday{
    color: red;
}

JavaScript

コード

全体のコードです。
次項で解説しますが、前回の投稿で解説した内容は割愛いたします。

const week = ["日", "月", "火", "水", "木", "金", "土"];
const today = new Date();
// 月末だとずれる可能性があるため、1日固定で取得
var showDate = new Date(today.getFullYear(), today.getMonth(), 1);

// 祝日取得
var request;
window.onload = function () {
    request = new XMLHttpRequest();
    request.open('get', 'syukujitsu.csv', true);
    request.send(null);
    request.onload = function () {
        // 初期表示
        showProcess(today, calendar);
    };
};

// 前の月表示
function prev(){
    showDate.setMonth(showDate.getMonth() - 1);
    showProcess(showDate);
}

// 次の月表示
function next(){
    showDate.setMonth(showDate.getMonth() + 1);
    showProcess(showDate);
}

// カレンダー表示
function showProcess(date) {
    var year = date.getFullYear();
    var month = date.getMonth(); // 0始まり
    document.querySelector('#header').innerHTML = year + "年 " + (month + 1) + "月";

    var calendar = createProcess(year, month);
    document.querySelector('#calendar').innerHTML = calendar;
}

// カレンダー作成
function createProcess(year, month) {
    // 曜日
    var calendar = "<table><tr class='dayOfWeek'>";
    for (var i = 0; i < week.length; i++) {
        calendar += "<th>" + week[i] + "</th>";
    }
    calendar += "</tr>";

    var count = 0;
    var startDayOfWeek = new Date(year, month, 1).getDay();
    var endDate = new Date(year, month + 1, 0).getDate();
    var lastMonthEndDate = new Date(year, month, 0).getDate();
    var row = Math.ceil((startDayOfWeek + endDate) / week.length);

    // 1行ずつ設定
    for (var i = 0; i < row; i++) {
        calendar += "<tr>";
        // 1colum単位で設定
        for (var j = 0; j < week.length; j++) {
            if (i == 0 && j < startDayOfWeek) {
                // 1行目で1日まで先月の日付を設定
                calendar += "<td class='disabled'>" + (lastMonthEndDate - startDayOfWeek + j + 1) + "</td>";
            } else if (count >= endDate) {
                // 最終行で最終日以降、翌月の日付を設定
                count++;
                calendar += "<td class='disabled'>" + (count - endDate) + "</td>";
            } else {
                // 当月の日付を曜日に照らし合わせて設定
                count++;
                var dateInfo = checkDate(year, month, count);
                if(dateInfo.isToday){
                    calendar += "<td class='today'>" + count + "</td>";
                } else if(dateInfo.isHoliday) {
                    calendar += "<td class='holiday' title='" + dateInfo.holidayName + "'>" + count + "</td>";
                } else {
                    calendar += "<td>" + count + "</td>";
                }
            }
        }
        calendar += "</tr>";
    }
    return calendar;
}

// 日付チェック
function checkDate(year, month, day) {
    if(isToday(year, month, day)){
        return {
            isToday: true,
            isHoliday: false,
            holidayName: ""
        };
    }

    var checkHoliday = isHoliday(year, month, day);
    return {
        isToday: false,
        isHoliday: checkHoliday[0],
        holidayName: checkHoliday[1],
    };
}

// 当日かどうか
function isToday(year, month, day) {
    return (year == today.getFullYear()
        && month == (today.getMonth())
        && day == today.getDate());
    }

// 祝日かどうか
function isHoliday(year, month, day) {
    var checkDate = year + '/' + month + '/' + day;
    var dateList = request.responseText.split('\n');
    // 1行目はヘッダーのため、初期値1で開始
    for (var i = 1; i < dateList.length; i++) {
        if (dateList[i].split(',')[0] === checkDate) {
            return [true, dateList[i].split(',')[1]];
        }
    }
    return [false, ""];
}

解説

先述したコードの解説です。

・祝日データ(CSVファイル)取得

// 祝日取得
var request;
window.onload = function () {
    request = new XMLHttpRequest();
    request.open('get', 'syukujitsu.csv', true);
    request.send(null);
    request.onload = function () {
      // 初期表示
      showProcess(today, calendar);
    };
};

XMLHttpRequestオブジェクトを使用して、変数 request にCSVデータを格納しています。
1. request.open()関数でデータ取込のリクエストを行います。
2. request.send()関数でリスエストをサーバに送信します。
3. 7~10行目はロードが成功して完了後に呼ばれます。

つまり、CSVファイルの読み込みが完了した後にカレンダー表示の関数(showProcess)を呼び出しています。

・当月の日付を設定 コード:70~77行目

var dateInfo = checkDate(year, month, count);
if(dateInfo.isToday){
    calendar += "<td class='today'>" + count + "</td>";
} else if(dateInfo.isHoliday) {
    calendar += "<td class='holiday' title='" + dateInfo.holidayName + "'>" + count + "</td>";
} else {
    calendar += "<td>" + count + "</td>";
}

1行目:変数 dateInfoに日付チェックの戻り値を設定します。

2~8行目:dateInfoを使って条件に応じて日付を設定していきます。

・dateInfo.isTodayがtrueの場合、クラスtodayを付与して設定。
dateInfo.isHolidayがtrueの場合、クラスholidayを付与、titleに祝日名を設定。
・上記以外の場合、日付をそのまま設定。

titleに祝日名を設定しているのは、祝日名をツールチップとして表示させるためです。
ツールチップ:カーソルやマウスポインタを合わせると小さな領域が注釈が表示されるもの。

・日付チェック checkDate関数

引数:year(年)、month(月)、day(日)
戻り値:日付の情報
・当日かどうか(Bool)
・祝日かどうか(Bool)
・祝日名(文字列)

// 日付チェック
function checkDate(year, month, day) {
    if(isToday(year, month, day)){
        return {
            isToday: true,
            isHoliday: false,
            holidayName: ""
        };
    }

    var checkHoliday = isHoliday(year, month, day);
    return {
        isToday: false,
        isHoliday: checkHoliday[0],
        holidayName: checkHoliday[1],
    };
}

日付チェックの関数です。
条件に応じて戻り値のオブジェクトを作成しています。

・当日判定 isToday関数

引数:year(年)、month(月)、day(日)
戻り値:当日かどうか(Bool)

// 当日かどうか
function isToday(year, month, day) {
    return (year == today.getFullYear()
    && month == (today.getMonth())
    && day == today.getDate());
}

today(Date型)を使って、当日かどうかを判定します。

・祝日判定 isHoliday関数

引数:year(年)、month(月)、day(日)
戻り値:配列 [祝日かどうか(Bool), 祝日名]

// 祝日かどうか
function isHoliday(year, month, day) {
    var checkDate = year + '/' + month + '/' + day;
    var dateList = request.responseText.split('\n');
    // 1行目はヘッダーのため、初期値1で開始
    for (var i = 1; i < dateList.length; i++) {
        if (dateList[i].split(',')[0] === checkDate) {
            return [true, dateList[i].split(',')[1]];
        }
    }
    return [false, ""];
}

3行目:CSVデータの日付はYYYY/MM/DD形式のため、
引数で渡された年月日をYYYY/MM/DD形式で変数checkDateに設定。

4行目:変数dataListには、CSVデータを行毎に配列に格納。

6~10行目:CSVの行数分for文を回します。
ファイルの1行目はヘッダー行のため、初期値=1で設定。

7~9行目:
祝日:dateList[i].split(‘,’)[0]
祝日名:dateList[i].split(‘,’)[1]

祝日とcheckDateが一致する場合、戻り値を配列 [true, 祝日名]で設定して処理を完了
どのデータとも一致しない場合は戻り値を配列 [false, 空文字]でセットして処理を完了。

デモ

こちらがデモページになります。
祝日の日付が赤くなっており、該当の日付にカーソルを合わせると祝日の名前が表示されます。

ただ、このCSVファイルは今年分までの日付しか入っていないため、
来年以降の祝日には対応されていません。
ご注意ください…。

最後に

いかがでしたでしょうか。
カレンダーを確認するときに祝日は分かっている方が見やすいですね♪

実装も簡単にできたのでお勧めです。

良ければ他の投稿もご覧ください!