スポンサーリンク
この記事の反省をふまえた続編もあります。この記事で私の書き換えたソースは、非常に無駄な事をしていますので、この記事だけだと恥ずかしいから、そちらもお読み頂ければ幸いです。
経過時間を計算してくれるタイマー
マスクドライダー17号さんが、彼のブログで面白いことをやっていて、最初にそのページを開いてからカウントダウンをはじめて、24時間経過したかどうか、残り時間を教えてくれるというスクリプトを走らせています。
その記事によると「教えて!gooにて回答されていた、babu_babooさんのScriptを参考にしました」ということで、babu_babu_babooさんのスクリプトをアレンジしたそうです。
教えて!gooの内容は、期限が来たかどうかカウントしたいだけなので、そのスクリプトは必要な目的を達成しています。
現在、マスクドライダー17号さんが使っているスクリプトは、おそらく24時間で正しく止まると思います。(止まるという表現は正確でないですがわかりやすく言っています)ただ、それを彼のブログで使う場合は不足している機能があります。それは、リセット機能です。
クッキーにデータが残っている
そのスクリプトは、クッキー(cookie)を使って、実現されています。ブラウザを閉じても、最初の時間をクッキーが記憶しているので、もう一度ブログにアクセスした時に、最初の時間からの残りを計算できます。そして、残り時間がなくなると「24時間以上経過しました!!」と表示して終了します。
カウントダウンが1度だけなら、今のままで、まったく問題ないのですが、マスクドライダー17号さんの場合は、そのタイマーを24時間経過する度に、リセットして使いまわしたいのだと思います。でも、今のままだと、最初の24時間が過ぎると「24時間以上経過しました!!」と表示されて、そのまま動かなくなります。その後、もう一度使おうとしても、24時間たってしまったので動きません。
繰り返しますが、教えて!gooの場合は、これで目的を達成出来ているので問題ありません。
現在、マスクドライダー17号さんは、クッキーの有効期限を10日にしているので、止まってから、さらに9日経てば、またスクリプトは使えるようになりますが、かといって、有効期限を短くすると、ブログを開いたときにスクリプトがリセットされてしまい、24時間経ったのかどうか、わからなくなります。ですから「24時間以上経過しました!!」という表示を読むまでは、リセットしたくありません。読み終わったら、ボタンを押して、リセット(カウントダウンの再スタート)をさせるのが、いちばんいいと思います。
元のソースコード
出来れば、動いている今のソースコードをそのまま流用したいです。ありがたいことに、機能ごとに、細かく関数に分けてくれています。この中のsetCookie関数を使えばクッキーはリセット出来ます。
<style type="text/css">
p#mess{ font-size:200%; }
</style>
<p id="mess">次回</p>
<script type="text/javascript">
<!--
(function (doc) {
function addDay ( day, date ) {
if( 'number' !== typeof day ) day = 0;
if( 'object' !== typeof date ) date = new Date;
date.setDate( date.getDate() + day );
return date;
}
function getCookie ( name ) {
name = encodeURIComponent( name ).replace( /([.*()]) /g, '\\$1' );
var value = doc.cookie.match( RegExp( name + '\\s*=\\s*(.*?)(?:[\\s;,]|$)' ) );
return value ? decodeURIComponent( value[1] ): '';
}
function setCookie ( name, value, day, path, domain ) {
return doc.cookie = encodeURIComponent (name) + '=' + encodeURIComponent (value) +
'; ' + 'expires=' + ( addDay(day) ).toUTCString () + '; ' + (path ? 'path=' + encodeURI (path) + '; ': '') +
(domain ? 'domain=' + encodeURI (domain) + '; ': '');
}
function padding ( n ) { return n < 10 ? '0' + n: n; }
var COOKIE_NAME = 'myCount';
var LIMIT_DAY = 1;
var SHELF_LIFE = 10;
var TIMEOUT_MESS = '24時間以上経過しました!!';
var node = doc.getElementById( 'mess' );
var targetDay = parseInt( getCookie( COOKIE_NAME ) );
if( !targetDay ) {
targetDay = addDay( LIMIT_DAY ).getTime();
setCookie( COOKIE_NAME, targetDay + '', SHELF_LIFE );
}
(function () {
var text = '次回まで約';
var s = (targetDay - (new Date).getTime()) / 1000 |0;
if (s < 0)
text = TIMEOUT_MESS;
else {
text += padding( s % 86400 / 3600 |0) + '時間' +
padding( s % 3600 / 60 |0) + '分' +
padding( s % 60 |0) + '秒です。';
setTimeout (arguments.callee, 1000);
}
node.firstChild.nodeValue = text;
})();
})(this.document);
//-->
</script>
しかし、弱ったことにソースは無名関数で括られているので、setCookie関数を使えません。無名関数の外に出してもいいのですが、この際、オブジェクト(クラス)化して、無名関数内のfunctionを、クラスのprototypeに移動させる事にします。
オブジェクト指向で書き換えたコード
関数の中身は、ほとんどいじっていませんし、スクリプトのロジックは一緒です。
<style type="text/css">
p#mess{ font-size:200%; }
</style>
<p id="mess"></p>
タイマーを<input type="button" onclick="countdown.reset();" value="リセット" />
<script type="text/javascript">
<!--
var timerCookie=function(id){
this.id=id;
this.COOKIE_NAME = 'myCount'+id;
this.LIMIT_DAY = 1;
this.SHELF_LIFE = 10;
this.TIMEOUT_MESS = '24時間以上経過しました!!';
this.targetDay = parseInt( this.getCookie( this.COOKIE_NAME ) );
if( !this.targetDay ) {
this.reset();
}else{
this.load();
}
};
timerCookie.prototype.addDay=function( day, date ){
if( 'number' !== typeof day ) day = 0;
if( 'object' !== typeof date ) date = new Date;
date.setDate( date.getDate() + day );
return date;
};
timerCookie.prototype.getCookie=function( name ){
name = encodeURIComponent( name ).replace( /([.*()]) /g, '\\$1' );
var value = document.cookie.match( RegExp( name + '\\s*=\\s*(.*?)(?:[\\s;,]|$)' ) );
return value ? decodeURIComponent( value[1] ): '';
};
timerCookie.prototype.setCookie=function( name, value, day, path, domain ){
return document.cookie = encodeURIComponent (name) + '=' + encodeURIComponent (value) +
'; ' + 'expires=' + ( this.addDay(day) ).toUTCString () + '; ' + (path ? 'path=' + encodeURI (path) + '; ': '') +
(domain ? 'domain=' + encodeURI (domain) + '; ': '');
};
timerCookie.prototype.padding=function( n ){ return n < 10 ? '0' + n: n; }
timerCookie.prototype.reset=function(){
this.targetDay = this.addDay( this.LIMIT_DAY ).getTime();
this.setCookie( this.COOKIE_NAME, this.targetDay + '', this.SHELF_LIFE );
this.load();
};
timerCookie.prototype.load=function () {
var text = '次回まで約';
var s = (this.targetDay - (new Date).getTime()) / 1000 |0;
if (s < 0)
text = this.TIMEOUT_MESS;
else {
text += this.padding( s % 86400 / 3600 |0) + '時間' +
this.padding( s % 3600 / 60 |0) + '分' +
this.padding( s % 60 |0) + '秒です。';
(function(obj){
setTimeout (function(){ obj.load(); }, 1000);
})(this);
}
document.getElementById( this.id ).innerHTML = text;
};
var countdown=new timerCookie('mess');
//-->
</script>
これで、setCookie関数を使えるようになりました。setCookie関数を使って作ったのが、上のprototypeにあるreset関数です。元のコードは本来、例文として作られたものですし、1回しか使わないという前提に立ったものです。そういう場合は無名関数で括ってしまうのが便利ですし、使いまわす時はオブジェクト化が便利だと思います。
ちなみに、オブジェクトは表示領域のidを引数にとるようにしています。これで、同じページで時間の違う、複数のタイマーを走らせることも出来ます。
タイマーを
上のソースコードは目的どおりに動くことを24時間たって確認しました。機能的には今のまま使って問題ありません。
ただ、babu_babu_babooさんから、上のソースについていくつかご指摘を頂きました。その内容は、ここのコメント欄にありますが次のような事です。それに沿って、上のソースを修正しようかと思ったのですが、元のソースを残しておかないと、指摘された内容が分からなくなると思うので、このままにしておきます。
- 指摘された内容
- オブジェクト指向なら、始まりは大文字で始めるのが通例
- オブジェクトを作るときに、new を使わずに、var obj = TimeCookie.create ();のような形にした方がよい
- prototypeに入れるべき関数とは?
この記事の反省をふまえた続編もあります。この記事で私の書き換えたソースは、非常に無駄な事をしていますので、この記事だけだと恥ずかしいから、そちらもお読み頂ければ幸いです。
関連コンテンツ
スポンサーリンク
>ここに、その書き込みの意図は?
こういう事に使いたい人に注目されるエントリかな~と思って。
なるほど。
babu_babu_babooさん
調べてみてもイマイチピンとこないのは
完全に私のレベルの問題です(^_^;)
お二方のお蔭で、作りたかったページを
構想通りに作ることができました。
私の文章力では上手く表現できませんが
お二方には本当に感謝しています。
すいません。
下のコメントに返信したつもりが失敗しちゃいました(+_+)