Presented by Nishishi via Movable Type. Last Updated: 2016/10/14. 13:32:46.

期限日時までリアルタイムにカウントダウンする

JavaScriptを使って、〆切(期限)日時まであとどのくらいの日数・時間があるのかをリアルタイムにカウントダウンし続ける機能を作ってみましょう。
指定の日時までにあと何ミリ秒あるのか?をJavaScriptで得る方法は、記事「指定日までの残り日数を計算して表示する」で解説しました。
また、一定間隔ごとに特定の処理を自動実行する方法は、記事「経過時間(秒数)をリアルタイムに表示する」で解説しました。
この2つを組み合わせれば、期限日時まで、ひたすらカウントダウンし続ける機能も簡単に作れます。

例えば、以下のように。サンプルとして日時が既に入っていますが、書き換えればその日時までをカウントダウンします。


※ここにカウントダウンが表示されます。

「上記の日時までカウントダウンする」ボタンをクリックすると、その瞬間に再計算されて表示します。……が、最初から1秒毎に自動更新しているので、ボタンを押さなくても最大1秒待つだけで再計算されてカウントダウン表示は更新されます。(^_^;)
上記でやっていることは、

  1. 現在日時を数値(1970-01-01 00:00:00からのミリ秒)に変換する。
  2. ユーザが入力した日時を取得して、数値(1970-01-01 00:00:00からのミリ秒)に変換する。
  3. 「現在日時の数値」と「指定日時の数値」を引き算して、日数(ミリ秒)の差を計算する。
  4. 差のミリ秒を、日数・時間・分・秒に分解して、カウントダウン表示用の文字列を作成する。
  5. 画面表示を更新。
  6. 上記1~5を、1秒毎に繰り返す。

という処理です。
他に、1桁の数字を2桁に調整する機能や、数字以外の文字が入力された場合には0として解釈する機能など、エラー対策も含んでいますが。

以下、簡単に解説してみます。(全体のソースは末尾に記載しています。)

カウントダウン日付の入力欄と、カウントダウン掲載領域

とりあえず、HTMLで以下の6個の入力欄を作っています。JavaScript側から参照するために、id属性を使って名前を付けています。

<input type="text" id="userYear" >年
<input type="text" id="userMonth">月
<input type="text" id="userDate" >日
<input type="text" id="userHour" >時
<input type="text" id="userMin" >分
<input type="text" id="userSec" >秒

あと、実際にカウントダウンを表示するための領域を以下のように確保しています。

<p id="RealtimeCountdownArea" >※ここにカウントダウンが表示されます。</p>

上記のid名「RealtimeCountdownArea」を使って、JavaScriptから表示を更新します。

現在日時と指定日時の差を求めて、残り日数・時間を表示

指定日時までの残り時間をカウントダウンするためには、「指定日時」から「現在日時」を引き算する必要があります。
その辺の処理に関しては、記事「指定日までの残り日数を計算して表示する」で解説していますから、詳しくはそちらをご参照下さい。
ここでは、以下のようにJavaScriptソースを記述しています。
かなり冗長なソースですが、「何をやっているのか?」の分かりやすさを重視して、短くせずに記述しています。実際に活用する際は、もっと短く書いた方が良いと思います。^^;
……と言いつつ、エラー処理部分は省略しているのですが。そこを含めるとソースを解釈しにくくなりそうなので。^^;(末尾のソースまとめには記載しています。)

▼現在日時

var nowDate = new Date();
var dnumNow = nowDate.getTime();

上記で、「現在日時」を数値(1970-01-01 00:00:00からのミリ秒)に変換して、変数dnumNowに格納しています。

▼ユーザの指定日時

var inputYear = document.getElementById("userYear").value;
var inputMonth = document.getElementById("userMonth").value - 1;
var inputDate = document.getElementById("userDate").value;
var inputHour = document.getElementById("userHour").value;
var inputMin = document.getElementById("userMin").value;
var inputSec = document.getElementById("userSec").value;
var targetDate = new Date( inputYear, inputMonth, inputDate, inputHour, inputMin, inputSec );
var dnumTarget = targetDate.getTime();

上記で、ユーザが入力した指定日時を、数値(1970-01-01 00:00:00からのミリ秒)に変換して、変数dnumTargetに格納しています。
Dateオブジェクトでは、月の指定は、「1月が0」・「2月が1」……「12月が11」になるので、1を引いています。

▼差を計算

var diff2Dates = dnumTarget - dnumNow;
if( dnumTarget < dnumNow ) {
   // 期限が過ぎた場合は -1 を掛けて正の値に変換
   diff2Dates *= -1;
}

上記で、2つの変数を引き算して、日数(ミリ秒)の差を計算しています。
期限が過ぎている場合はマイナスの値になるので、その場合には -1 を掛けて正の値にしています。

▼ミリ秒を分解

var dDays = diff2Dates / ( 1000 * 60 * 60 * 24 ); // 日数
diff2Dates = diff2Dates % ( 1000 * 60 * 60 * 24 );
var dHour = diff2Dates / ( 1000 * 60 * 60 ); // 時間
diff2Dates = diff2Dates % ( 1000 * 60 * 60 );
var dMin = diff2Dates / ( 1000 * 60 ); // 分
diff2Dates = diff2Dates % ( 1000 * 60 );
var dSec = diff2Dates / 1000; // 秒

上記で、「差のミリ秒」を、日数・時間・分・秒に分解しています。
「割った解」と「割った余り」を使っていくことで、日→時→分→秒の順に抜き出しています。
日数は変数dDaysに、時間は変数dHourに、分は変数dMinに、秒は変数dSecに入ります。
なんかもうちょっとスマートな方法があるような気がしますが(^_^;)、まあ上記でもちゃんと計算はできます。

カウントダウンの画面表示

次に、計算結果を画面に表示する必要があります。
冒頭のサンプルでは、ユーザが入力した日時も含めて表示しています。
それらのソースは以下の通りです。

▼期限日時を表示

var dlYear = targetDate.getFullYear();
var dlMonth = targetDate.getMonth() + 1;
var dlDate = targetDate.getDate();
var dlHour = targetDate.getHours();
var dlMin = targetDate.getMinutes();
var dlSec = targetDate.getSeconds();
var msg1 = "期限の" + dlYear + "/" + dlMonth + "/" + dlDate + " " + dlHour + ":" + dlMin + ":" + dlSec;

上記で、変数msg1に、ユーザが指定した日時が文字列として格納されます。
実際には、「1桁の数字を2桁固定にする」処理を含んでいますが、上記では見た目を簡単にするために省略しています。
先も述べましたが、月は「1月が0」・「2月が1」……「12月が11」になっているので、ここでは1を足しています。
ユーザが入力した文字列を直接は利用せずに、カウントダウン計算用のDateオブジェクトから日付を求めているのは、ユーザの入力内容をそのまま出力する危険を避けるためです。

▼残り日数・時間を表示

var msg2 = Math.floor(dDays) + "日" + Math.floor(dHour) + "時間" + Math.floor(dMin) + "分" + Math.floor(dSec) + "秒";

上記で、変数msg2に、残り日数・時間が文字列として格納されます。ここがカウントダウンのメインです。
なお、Math.floorは、小数点以下を取り去る関数です。これがないと、小数点以下がずらーっと並んでしまいます。

▼カウントダウンかカウントアップの文字列を作成

var msg;
if( dnumTarget > dnumNow ) {
   // まだ期限が来ていない場合
   msg = msg1 + "までは、あと" + msg2 + "です。";
}
else {
   // 期限が過ぎた場合
   msg = msg1 + "は、既に" + msg2 + "前に過ぎました。";
}

上記で、画面表示用の文字列を作成し、変数msgに格納しています。
「期限が来ていない」場合と「期限が過ぎた場合」とで表現を変える必要があったので、if文で条件分岐させています。

document.getElementById("RealtimeCountdownArea").innerHTML = msg;

最後に、上記のようにinnerHTMLメソッドを使って、作成した文字列を(id名「RealtimeCountdownArea」の領域に)表示しています。
上記の一連の処理を、「showCountdown」という名称で関数にしておけば完成です。(全体のソースは後述)

1秒ごとに処理を繰り返すことで、リアルタイムにカウントダウンする

上記で作成したソースを、1秒毎に繰り返し自動実行することで、「リアルタイムのカウントダウン」が実現します。
一定間隔ごとに特定の処理を自動実行する方法は、記事「経過時間(秒数)をリアルタイムに表示する」で解説しましたから、詳しくはそちらをご参照下さい。
ここでは、以下のように1行を書いただけです。

setInterval('showCountdown()',1000);

これで、1000ミリ秒(=1秒)ごとにshowCountdown関数が実行されます。

リアルタイムカウントダウン機能を作るソースのまとめ

基本的に今回のサンプルは、2つの記事で解説済みの内容を組み合わせただけです。
なので、説明はあまり詳しくは書きませんでした。
全体的に対して難しくないので、ソースを読んで頂ければ簡単に分かるとは思いますが。(^_^;)
以下に、すべてのソース(HTML+JavaScript)をまとめて掲載しておきます。上記では省略した、桁数調整や入力エラー対策の関数も含んでいます。

<p>
   <input type="text" id="userYear" maxlength="4">年
   <input type="text" id="userMonth" maxlength="2">月
   <input type="text" id="userDate" maxlength="2">日
   <input type="text" id="userHour" maxlength="2">時
   <input type="text" id="userMin" maxlength="2">分
   <input type="text" id="userSec" maxlength="2">秒<br>
   <input type="button" value="上記の日時までカウントダウンする" onclick="showCountdown();" />
</p>
<p id="RealtimeCountdownArea">※ここにカウントダウンが表示されます。</p>

<script type="text/javascript">
function set2fig(num) {
   // 数値が1桁だったら2桁の文字列にして返す
   var ret;
   if( num < 10 ) { ret = "0" + num; }
   else { ret = num; }
   return ret;
}
function isNumOrZero(num) {
   // 数値でなかったら0にして返す
   if( isNaN(num) ) { return 0; }
   return num;
}
function showCountdown() {
   // 現在日時を数値(1970-01-01 00:00:00からのミリ秒)に変換
   var nowDate = new Date();
   var dnumNow = nowDate.getTime();

   // 指定日時を数値(1970-01-01 00:00:00からのミリ秒)に変換
   var inputYear = document.getElementById("userYear").value;
   var inputMonth = document.getElementById("userMonth").value - 1;
   var inputDate = document.getElementById("userDate").value;
   var inputHour = document.getElementById("userHour").value;
   var inputMin = document.getElementById("userMin").value;
   var inputSec = document.getElementById("userSec").value;
   var targetDate = new Date( isNumOrZero(inputYear), isNumOrZero(inputMonth), isNumOrZero(inputDate), isNumOrZero(inputHour), isNumOrZero(inputMin), isNumOrZero(inputSec) );
   var dnumTarget = targetDate.getTime();

   // 表示を準備
   var dlYear = targetDate.getFullYear();
   var dlMonth = targetDate.getMonth() + 1;
   var dlDate = targetDate.getDate();
   var dlHour = targetDate.getHours();
   var dlMin = targetDate.getMinutes();
   var dlSec = targetDate.getSeconds();
   var msg1 = "期限の<span>" + dlYear + "/" + dlMonth + "/" + dlDate + " " + set2fig(dlHour) + ":" + set2fig(dlMin) + ":" + set2fig(dlSec);

   // 引き算して日数(ミリ秒)の差を計算
   var diff2Dates = dnumTarget - dnumNow;
   if( dnumTarget < dnumNow ) {
      // 期限が過ぎた場合は -1 を掛けて正の値に変換
      diff2Dates *= -1;
   }

   // 差のミリ秒を、日数・時間・分・秒に分割
   var dDays = diff2Dates / ( 1000 * 60 * 60 * 24 );   // 日数
   diff2Dates = diff2Dates % ( 1000 * 60 * 60 * 24 );
   var dHour = diff2Dates / ( 1000 * 60 * 60 );   // 時間
   diff2Dates = diff2Dates % ( 1000 * 60 * 60 );
   var dMin = diff2Dates / ( 1000 * 60 );   // 分
   diff2Dates = diff2Dates % ( 1000 * 60 );
   var dSec = diff2Dates / 1000;   // 秒
   var msg2 = Math.floor(dDays) + "日" + Math.floor(dHour) + "時間" + Math.floor(dMin) + "分" + Math.floor(dSec) + "秒";

   // 表示文字列の作成
   var msg;
   if( dnumTarget > dnumNow ) {
      // まだ期限が来ていない場合
      msg = msg1 + "までは、あと" + msg2 + "です。";
   }
   else {
      // 期限が過ぎた場合
      msg = msg1 + "は、既に" + msg2 + "前に過ぎました。";
   }

   // 作成した文字列を表示
   document.getElementById("RealtimeCountdownArea").innerHTML = msg;
}
// 1秒ごとに実行
setInterval('showCountdown()',1000);
</script>

上記では、一定時間毎に処理を繰り返すsetIntervalメソッドをいきなり実行しているので、スクリプトの読み込みと同時に処理が開始されます。
それを避けたい場合は、setIntervalメソッドも何らかの関数の内側に記述して下さい。記事「経過時間(秒数)をリアルタイムに表示する」では、ボタンをクリックするまでsetIntervalメソッドを実行しない書き方をしています。

showCountdown関数を実行するためのボタンも用意していますが、押さなくても1秒ごとに自動実行されるので、まあ不要です。(^_^;)
ページの読込と同時にカウントダウンを実行させるのでないのなら、カウントダウンを開始するボタンが必要ですが。

ミリ秒まで表示させてカウントダウンする場合

なお、期限日時が来たとき、「残り0日0時間0分0秒です」→「0日0時間0分0秒前に過ぎました」で、合計2秒間あるように見えます。
なぜこうなるのかは、ミリ秒まで表示させてみるとよく分かります。(^_^;;;

  • 残り1秒000 → 「残り1秒です」と表示
  • 残り0秒999 → 「残り0秒です」と表示
  • 残り0秒001 → 「残り0秒です」と表示
  • 0秒001経過 → 「0秒前に過ぎました」と表示
  • 0秒999経過 → 「0秒前に過ぎました」と表示
  • 1秒000経過 → 「1秒前に過ぎました」と表示

上記のようになるので、「指定日時まで残り1秒を切った後」から「指定日時から1秒が経過する直前」までの2秒間は、「0秒」の表示になります。
若干、不思議な感じがするので、それを避けたければ、ミリ秒まで含めて表示するのも良いかも知れません。
変数msg2を作る行を以下のようにした上で、

var dMiSec = diff2Dates % 1000;
var msg2 = Math.floor(dDays) + "日" + Math.floor(dHour) + "時間" + Math.floor(dMin) + "分" + Math.floor(dSec) + "秒" + dMiSec;

以下のように、setIntervalメソッドを1秒よりももっと短い間隔で呼び出します。

setInterval('showCountdown()',200);

上記の場合は、200ミリ秒(=0.2秒)ごとに描画が更新されます。
ただ、あまり更新頻度を上げてしまうと、処理が重たくなってしまう可能性がありますから、あんまり頻度を上げすぎない(=数値を低くし過ぎない)方が良いとは思います。

……というわけで、なんだかほとんどソースだけを掲載したような感じですが。(^_^;)
まあ、解説は冒頭でリンクしている2本の記事に書いていますからね。^^; 重複分を除けば、ほとんど説明することなんてないので。(^_^;;;

ユーザの指定した日時までの残り日数・時間をリアルタイムにカウントダウンする機能の作り方でした。(期限が過ぎた場合は、カウントダウンではなくカウントアップですが。^^;)
ぜひ、何かに活用してみて下さい。(^_^;)

JavaScript TIPS 主要なカテゴリ

現在、以下のカテゴリに区分してTIPSを公開しています。

  • イベント : JavaScriptを実行するトリガーとなるイベントに関するTIPS
  • 入力フォーム : JavaScriptで入力フォームを扱う方法に関するTIPS
  • 情報取得 : JavaScriptで様々な情報を得る方法に関するTIPS
  • 操作・移動 : JavaScriptでブラウザを操作したり表示ページを移動したりする方法に関するTIPS
  • 日付・時刻 : JavaScriptで日付や時刻を扱う方法に関するTIPS
  • 機能 : JavaScriptで何らかの機能を実現する方法に関するTIPS
  • 装飾・内容変更 : JavaScriptで装飾や内容を変更する方法に関するTIPS
  • 計算・変換 : JavaScriptで様々な計算や変換処理を行う方法に関するTIPS

▲各カテゴリ毎に、TIPSのタイトルと概要を一覧できます。ぜひ、いろいろ覗いてみて下さい!

関連書籍・関連ソフトなど