はじめに
Delphi + ActiveXで作られた既存Webアプリケーションの仕様違い版を新規作成することになり、この際なので、ASP.NET + Bootstrap 4 で作成することにしました。
表入力形式や一覧表示にはスプレッドシートの「Handsontable」を使うとして、カレンダー入力は当初「Pikaday.js」を考えていましたが、画面設計に時間入力があり「DatetimePicker.js」ならカレンダー入力と時間入力の2つの機能を満たせるため、こちらに変更しました。
時間入力は、既存Webアプリケーションを踏襲した古い画面デザインで時間コンボボックスと分コンボボックスで入力する設計になっていたため、DatetimePickerの時間入力を使った今風なWebデザインに変更することにしました。
【2021/05/12追記】
2021年5月6日にBootstrap 5が正式リリースしたので、Bootstrap 5用の記事を更新しました。
Bootstrap 4 DatetimePicker
Bootstrap DatetimePicker は、Bootstrap 3までとなっています。
https://eonasdan.github.io/bootstrap-datetimepicker/
Bootstrap 4でも使えると思ったのですが、Bootstrap 4 と Bootstrap 3 では変更点が多く動作させることができませんでした。
Bootstrap 3でDatetimePickerを使いたい
世界中の誰かが対応しているんじゃないかとネットで調べて見つけました。
https://github.com/pingcheng/bootstrap4-datetimepicker
Customizable Date/Time Picker Component For Bootstrap 4 (デモ)
【2018/07/14追記】
なんとISSUEまで書いたのに、本家の本当の後継を見つけてしまいました。当然、前月と次月のツールチップスの不具合もありません。
https://github.com/tempusdominus/bootstrap-4
Customizable Date & Time Picker For Bootstrap 4 (デモ)
同じ作者がBootstrapに依存しないDatetimePickerも作成しています。
https://github.com/Eonasdan/tempus-dominus
以下の記事は、最初に見つけたpingcheng氏のbootstrap4-datetimepickerのままですが、日本語化などは同じように使えると思います。
日本語化
Customizable Date/Time Picker Component For Bootstrap 4からDownloadしたファイルを日本語向けに変更しました。
カレンダー表示を日本語向けにするには、moment.jsのローカル版が必要となるため、「moment.js/2.22.2/locale/ja.js」を追加し、ついでにバージョンも最新にしました。全ての言語対応版の「moment-with-locales.min.js」でも動作します。
https://cdnjs.com/libraries/moment.js/
カレンダー表示時に土日に色が付くようCSSを設定してあります。
プログラム(pingcheng氏版)
※ToolTipsの日本語名は最低限にしてありますので、必要なのは追加してください。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Date/Time Picker Component For Bootstrap 4 Demo</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/4.0.0/flatly/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="build/css/bootstrap-datetimepicker.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/locale/ja.js" type="text/javascript"></script>
<script src="build/js/bootstrap-datetimepicker.min.js"></script>
<style type="text/css">
.datepicker-days th.dow:first-child,
.datepicker-days td:first-child {
color: #f00;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #00f;
}
</style>
</head>
<body>
</br>
<div class="container">
<div class="row">
<div class="form-group">
<div class="input-group date" id="datetimepicker1">
<label for="datetimepicker1" class="pt-2 pr-2">日付:</label>
<input type="text" class="form-control" />
<span class="input-group-append">
<span class="input-group-text"><i class="fa fa-calendar"></i></span>
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="input-group date" id="datetimepicker2">
<label for="datetimepicker2" class="pt-2 pr-2">時間:</label>
<input type="text" class="form-control" />
<span class="input-group-append">
<span class="input-group-text"><i class="fa fa-clock-o"></i></span>
</span>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({
dayViewHeaderFormat: 'YYYY年 MMMM',
tooltips: {
close: '閉じる',
selectMonth: '月を選択',
prevMonth: '前月',
nextMonth: '次月',
selectYear: '年を選択',
prevYear: '前年',
nextYear: '次年',
selectTime: '時間を選択',
selectDate: '日付を選択',
prevDecade: '前期間',
nextDecade: '次期間',
selectDecade: '期間を選択',
prevCentury: '前世紀',
nextCentury: '次世紀'
},
format: 'YYYY/MM/DD',
locale: 'ja',
showClose: true
});
$('#datetimepicker2').datetimepicker({
tooltips: {
close: '閉じる',
pickHour: '時間を取得',
incrementHour: '時間を増加',
decrementHour: '時間を減少',
pickMinute: '分を取得',
incrementMinute: '分を増加',
decrementMinute: '分を減少',
pickSecond: '秒を取得',
incrementSecond: '秒を増加',
decrementSecond: '秒を減少',
togglePeriod: '午前/午後切替',
selectTime: '時間を選択'
},
format: 'HH:mm',
locale: 'ja',
showClose: true
});
});
</script>
</body>
</html>
補足
ToolTipsの前月 or 前年や次月 or 次年が表示されません。本家は表示されるので、Bootstrap 4対応の不具合と思われます。
Pull requestで指摘されていますが、カレンダー表示ボタンをクリックしてもカレンダーが表示されません。
Fix bootstrap 4 css class #6
これはBootstrap 4では「input-group-addon」ではなく「input-group-append」に変わったのですが、現段階でダウンロードしたファイルには反映されていませんでした。
今回はプログラムは変更しないで、classに両方書くことで対応しました。
<span class="input-group-append input-group-addon">
<span class="input-group-text"><i class="fa fa-clock-o"></i></span>
</span>
【2018/07/14追記】
最新版では指摘されたPull requestがマージされ「input-group-addon」ではなく「input-group-append」に変更になりました。
よって、「input-group-append」のみでいいです。ソースも反映しました。
<span class="input-group-append">
<span class="input-group-text"><i class="fa fa-clock-o"></i></span>
</span>
【2018/07/08追記】
Bootstrap 4対応版は本家と色を微妙に変えているようで、選択日は本家の方が自分は好みなので、CSSに追加してみました。
<style type="text/css">
.datepicker-days th.dow:first-child,
.datepicker-days td:first-child {
color: #f00;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #00f;
}
.bootstrap-datetimepicker-widget table td.active,
.bootstrap-datetimepicker-widget table td.active:hover {
background-color: #337ab7;
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td span.active,
.bootstrap-datetimepicker-widget table td i.active {
background-color: #337ab7;
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
</style>
改良して欲しい点
GitHubなので改良したい点があれば、Pull requestするなりforkすればいいんですけど、Pikaday.js を最初に検証したのでオプション不足が見えてしまう。
- 日本は工場主導なので月曜日始まり対応(firstDay)のカレンダー表示が欲しい
- 月選択に切り替えた時のタイトルには"年"が付いてないので、"年"表示(yearSuffix) が欲しい
// first day of week (0: Sunday, 1: Monday etc)
firstDay: 0,
// Additional text to append to the year in the calendar title
yearSuffix: '年',
ISSUE
初のISSUEを書いてみました。
https://github.com/pingcheng/bootstrap4-datetimepicker/issues
前月と次月のツールチップの不具合の報告と、本家でも同じ不具合でISSUE「タイムピッカー のカレンダーアイコンのツールチップが正しくありません」は修正ソースコードを記載しました。
【2018/07/14追記】
前月と次月のツールチップが表示されない原因が分かりました。
find('span') を find('i') に置換する必要があります。
daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth);
daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth);
daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth);
↓
daysViewHeader.eq(0).find('i').attr('title', options.tooltips.prevMonth);
daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth);
daysViewHeader.eq(2).find('i').attr('title', options.tooltips.nextMonth);
※前月と次月以外にも前年と次年などもありますが、ISSUEに書いたので省略
Bootstrap 4 DatetimePicker(tempusdominus氏版)
後で見つけた、tempusdominus氏のbootstrap4-datetimepicker も試してみました。
本家の後継という位置づけなのでドキュメントも整っています。
Bootstrap4 v5 Docs
インストール
npm i tempusdominus-bootstrap-4
Package Installerの導入
NuGet版が用意されていないため、Visual Studioの場合は Package Installerをインストールします。
ツール->拡張機能と更新プログラムから拡張機能と更新プログラムの画面を開きます。オンラインを選択し、package installerを検索します。
インストール完了後(Visual Studio再起動)に、プロジェクトの右クリックメニューで「Quick Install Package」を選択します。
画面表示後に「npm」を選択、「tempusdominus-bootstrap-4」を入力してバージョンを最新にしたら「Install」ボタンをクリックします。
適切なフォルダに配置します。
プログラム(tempusdominus氏版)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Tempus Dominus Bootstrap 4 Date & Time Picker Examples</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/4.1.1/flatly/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="build/css/tempusdominus-bootstrap-4.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/locale/ja.js" type="text/javascript"></script>
<script src="build/js/tempusdominus-bootstrap-4.js"></script>
<style type="text/css">
.datepicker-days th.dow:first-child,
.datepicker-days td:first-child {
color: #f00;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #00f;
}
</style>
</head>
<body>
</br>
<div class="container">
<div class="row">
<div class="form-group">
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
<label for="datetimepicker1" class="pt-2 pr-2">日付:</label>
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1" />
<span class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
<span class="input-group-text"><i class="fa fa-calendar"></i></span>
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="input-group date" id="datetimepicker2" data-target-input="nearest">
<label for="datetimepicker2" class="pt-2 pr-2">時間:</label>
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker2" />
<span class="input-group-append" data-target="#datetimepicker2" data-toggle="datetimepicker">
<span class="input-group-text"><i class="fa fa-clock-o"></i></span>
</span>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({
dayViewHeaderFormat: 'YYYY年 MMMM',
tooltips: {
close: '閉じる',
selectMonth: '月を選択',
prevMonth: '前月',
nextMonth: '次月',
selectYear: '年を選択',
prevYear: '前年',
nextYear: '次年',
selectTime: '時間を選択',
selectDate: '日付を選択',
prevDecade: '前期間',
nextDecade: '次期間',
selectDecade: '期間を選択',
prevCentury: '前世紀',
nextCentury: '次世紀'
},
format: 'YYYY/MM/DD',
locale: 'ja',
buttons: {
showClose: true
}
});
$('#datetimepicker2').datetimepicker({
tooltips: {
close: '閉じる',
pickHour: '時間を取得',
incrementHour: '時間を増加',
decrementHour: '時間を減少',
pickMinute: '分を取得',
incrementMinute: '分を増加',
decrementMinute: '分を減少',
pickSecond: '秒を取得',
incrementSecond: '秒を増加',
decrementSecond: '秒を減少',
togglePeriod: '午前/午後切替',
selectTime: '時間を選択'
},
format: 'HH:mm',
locale: 'ja',
buttons: {
showClose: true
}
});
});
</script>
</body>
</html>
補足(tempusdominus氏版)
今のところ「showClose: true」にしていますが、クローズボタンが表示されません。
ここはISSUEを書いて報告すれば修正されると思います。
https://github.com/tempusdominus/bootstrap-4/issues/160
Other changes
moved showTodayButton, showClear and showClose into options.buttons
buttons配下に移動になっていました。
Font Awesome Ver.5対応
【2020/08/10追記】
Font Awesome Ver.5にした場合、時間アイコンが表示されなくなります。これはVer.4以前で使用していたアイコン「fa-clock-o」がVer.5から「fa-clock」に変わっているためです。
アイコン種類 | ver.4 | ver.5 |
---|---|---|
カレンダー | fa fa-calendar | far fa-calendar-alt |
時計 | fa fa-clock-o | far fa-clock |
アイコンの絵柄は下記サイトを参照して好みのものにしてください。
https://www.w3schools.com/icons/fontawesome5_icons_datetime.asp
オプションで新しいアイコンを直接指定するように修正する。
locale: 'ja',
icons: {
time: 'far fa-clock',
date: 'far fa-calendar-alt',
up: 'fas fa-arrow-up',
down: 'fas fa-arrow-down'
}
ついでに各ライブラリーのバージョンを新しくするのもいいでしょう。
ライブラリー | バージョン |
---|---|
bootstrap 4 | 4.5.2 |
tempusdominus-bootstrap-4 | 5.1.2 |
font awesome | 5.14.0 |
jquery | 3.5.1 |
popper.js | 1.16.1 |
moment.js | 2.27.0 |
<head>
<meta charset="utf-8">
<title>Tempus Dominus Bootstrap 4 Date & Time Picker Examples</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/css/tempusdominus-bootstrap-4.min.css">
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/js/all.min.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/locale/ja.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/js/tempusdominus-bootstrap-4.min.js"></script>
<style type="text/css">
.datepicker-days th.dow:first-child,
.datepicker-days td:first-child {
color: #f00;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #00f;
}
.input-group-text {
height: 38;
}
</style>
</head>
今回の対応をした場合ボタンの部分がエディタより少し大きく表示されるため、CSSのinput-group-textの高さを調整しました。
改良して欲しい点を直した
【2018/07/28追記】
上記で改良して欲しい点を書きましたが、オープンソースなので修正を待つより直してしまえってことでやってみました。
firstDay Option
日本は工場主導なので月曜日始まり対応(firstDay)のカレンダー表示にする。
firstDay のDayって日のことかと思って違和感があったけど、調べたら曜日のことでした。
//first day of week (0: Sunday, 1: Monday etc)
firstDay: 1,
ソースコードの変更点は、ISSUES に書きました。
https://github.com/tempusdominus/bootstrap-4/issues/173
【2018/08/19追記】
作者によりCloseされました。理由はmoment.js側でfirstDayが制御できるとのこと。
調べてみたら、下記の方法で出来ることが分かりました。
Bootstrap 3 Datetimepicker 3.0.0 - week starts at Monday
locale: 'ja' としているところを書き換えます。
locale: moment.locale('ja', {
week: { dow: 1 }
}),
土日の色付けは下記のようにしました。
<style type="text/css">
.datepicker-days th.dow:nth-last-child(2),
.datepicker-days td:nth-last-child(2) {
color: #00f;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #f00;
}
</style>
yearSuffix Option
月選択に切り替えた時のタイトルには"年"が付いてないので、"年"表示(yearSuffix) を付けました。
yearSuffix: '年',
dayViewHeaderFormat: 'YYYY{0} MMMM',
ソースコードの変更点は、ISSUES に書きました。
https://github.com/tempusdominus/bootstrap-4/issues/174
2018/08/19に修正したコードをgithubに上げました。
https://github.com/yaju/Bootstrap-4-Date-Time-Picker
beforeShowDay Option
【2018/09/18追記】
土日以外に祝日に色をつけたい場合、下記サイトのbootstrap datepickerのような日ごとに呼ばれるbeforeShowDay関数はありませんでした。
bootstrap datepickerで土日の色を変えるよ!
プログラムを修正してbeforeShowDayオプションを追記するとかもしてみたが、最終的にシンプルに土日の色付けと同様にCSSに追加するだけで出来ることが分かりました。
※data-day=(日付)
の日付は書式(format: 'YYYY/MM/DD')に則ります。
<style type="text/css">
.datepicker-days th.dow:first-child,
.datepicker-days td:first-child {
color: #f00;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #00f;
}
.datepicker-days td[data-day="2018/09/17"] {
color: #f00;
}
</style>
改変方法
プログラムを修正してbeforeShowDayオプションを追加する方法となります。
CSSのみでは対応し難い場合もありますので、日ごとに呼ばれるコールバック関数があると便利です。
// 170行あたりにオプションを追加
beforeShowDay: $.noop,
// 下記の関数内にbeforeShowDayオプション機能を追加
TempusDominusBootstrap4.prototype._fillDate = function _fillDate() {
for (i = 0; i < 42; i++) {
// ・・・
if (currentDate.day() === ((0 + this._options.firstDay) % 7) || currentDate.day() === ((6 + this._options.firstDay) % 7)) {
clsName += ' weekend';
}
// ここから追加
if (this._options.beforeShowDay !== $.noop) {
var before = this._options.beforeShowDay(currentDate);
if (before !== '') clsName += ' ' + before;
}
// ここまで
row.append('<td data-action="selectDay" data-day="' + currentDate.format('L') + '" class="day' + clsName + '">' + currentDate.date() + '</td>');
currentDate.add(1, 'd');
}
}
【使い方】
beforeShowDayオプション用の関数を追加します、引数のdateは、Moment.jsのdate型となります。
Moment.jsはJavaScriptのDateオブジェクトをラップするオブジェクトとなっていますので、toDate()を呼べばDate型に変換できます。
CSSのクラス名を追加するのが目的です。
$('#datetimepicker1').datetimepicker({
format: 'YYYY/MM/DD',
beforeShowDay: function(date) {
if(date.format('L') === "2018/09/17") {
console.log(date.format('L'));
return "public-holiday";
}
else
return "";
}
});
イベント
変更イベントをネット調べると「dp.change」とあったが動作せず、これはBootstrap3 のDatetimePickerで、tempusdominus版のDatetimePickerは「change.datetimepicker」であった。ドキュメントのEventsには記載してある。
$('#datetimepicker1').on('change.datetimepicker', function(e) {
console.log(e.date);
});
インラインの値変更
HandsontableというWEBでExcelのようなスプレッドシートライクな入力を可能にしてくれるJavaScriptライブラリがあります。
日付入力は標準でありますが時間入力がありません。そこでボタンをクリックしたらダイアログ画面内にインラインの時間入力を表示するようにしました。
// ボタンの時間をインラインのデフォルト時間にセット
var time = table.getDataAtCell(selectedRow, selectedCol);
if (time === null) time = "00:00";
$('#datetimepicker1').data("datetimepicker").defaultDate(moment(moment().format('L') + ' ' + time));
// 変更イベントでボタンの時間表示に即時反映
$('#datetimepicker1').on('change.datetimepicker', function(e) {
if(selectedCol === 0) return;
table.setDataAtCell(selectedRow, selectedCol, e.date.format("HH:mm"));
});
Set a default date #111 - tempusdominus/bootstrap-4
最後に
調べると思ってたより記事が無いとかあっても古くて使えなかったり、結局苦労して切り開いて率先して書いていくしかなんだよね。
まだまだ、グーグル先生って頼りない。