リストに1ヶ月で消えるNewマークをつけるjavascriptを書いたわけだが

newmark.js某サイトで「新着のNewマーク、つけたりけしたりチェックうぜー!」ということで。めんどいのでひたすらググってみたものの、そう簡単に用途に合うものが見つかるものじゃなく。ひさしぶりにjavascriptに取り組んでみました。と言いましても、こちらの 「一定時間で自動的に消える New マークを付ける JavaScript の jQuery 版 – かたつむりくんのWWW」 を元ネタに改変してみたわけなんですが。よろしければお使い下さい。再改変等についてはサイト様に準じます。

改変のポイントは、以下の通りです。

  • Newの表示は1ヶ月間で。時よりもっと長くして。
  • 1ヶ月だと単純に30日後じゃダメなんだな。あと、月末が28日とか30日とか困るなあ。
  • jQuery版ですか。ウチ使ってないから非使用に改造。
  • HTML汚染回避はそのまま。<li>のtitleに年月日を仕込むことに。

完成したソースコードは次のようになります。

window.onload = function (){
	// display content
	var content = '<img class="newmark" src="/img/icon/newmark.gif" alt="1ヶ月以内のお知らせ" width="15" height="15" title="1ヶ月以内のお知らせ"/>';
	var currentDate = new Date();	// Now date

	// 全てのLIタグの中身を取得
	var lists = document.getElementsByTagName("li");
	for(var i=0; i < lists.length; ++i){
		var newmarkAttr = lists[i].getAttribute('title'); //titleをさがす
		if (newmarkAttr !== null && newmarkAttr !== '') {
			newmarkAttr = newmarkAttr.replace(/年|月/g,':');  //年月日を
			newmarkAttr = newmarkAttr.replace(/\s|日.*/g,''); //数字配列に
			var time = newmarkAttr.split(":");
			var nextMon = eval(time[1])+1;	//来月は?
			// 次月0日 → 前月末日
			var endDay = new Date(time[0], nextMon, 0);
			endDay = endDay.getDate();	//末日数値を代入
			var day = eval(time[2]);	//設定日を数値化して代入
			if(day > endDay) day = endDay; //翌月設定日 > 翌月末日
			var expireDate = new Date(time[0], time[1], day);
			if(expireDate >= currentDate){
				lists[i].innerHTML = lists[i].innerHTML + content;
			}
		}
	}
}

これを newmark.js などのファイル名で保存し、サーバにアップします。表示するサイトの文字コードがUTF-8なら、このjsファイルもUTF-8にしてください。でHTMLの<head>内で読み込みます。

<script type="text/javascript" src="newmark.js" charset="UTF-8"></script>

しかる後、Newマークを表示させたい場所の<li>の中に「 <li title="2010年3月3日"> 」などと仕込むと、</li>の直前にNewマークが挿入されて表示されます。

解説(というか改変へのヒント)

まず1行目は、「ページを読み込み終わったら動作させますよ」というおまじないです。

window.onload = function (){

2~3行目は、実際表示させるNewマークのHTML要素ですね。ファイル名や要素は適宜変更ください。

// display content
var content = '<img class="newmark" src="/img/icon/newmark.gif" alt="1ヶ月以内のお知らせ" width="15" height="15" title="1ヶ月以内のお知らせ"/>';

4行目では、現在時刻を取得しています。

var currentDate = new Date();	// Now date

6~7行目では、HTML内の全ての<li>を調べ、その中身を配列に格納しています。

// 全てのLIタグの中身を取得
var lists = document.getElementsByTagName("li");

さ、これから中身を精査します。

for(var i=0; i < lists.length; ++i){

9行目では、その中から「title」要素の中身を取り出しています。

var newmarkAttr = lists[i].getAttribute('title'); //titleをさがす

10行目は、9行目で何か取得できたら?と言う判定です。

if (newmarkAttr !== null && newmarkAttr !== '') {

11~13行目では、取得したものを整形して、年月日の配列にしています。ホントはこの後、有効な年月日であるかどうかのチェックをした方が良いのでしょうけど。

newmarkAttr = newmarkAttr.replace(/年|月/g,':');  //年月日を
newmarkAttr = newmarkAttr.replace(/\s|日.*/g,''); //数字配列に
var time = newmarkAttr.split(":");

14行目では、翌月の月数字を(数値化して)取り出しています。ここで1ヶ月後なので+1ですが、3ヶ月後なら+3でいいのでしょうね。

var nextMon = eval(time[1])+1;	//来月は?

15~17行目では、来月の月末日を求めています。Date関数で日のところが0だと前月末日を返すそうです。このあたりの処理はhoge256ブログ様の記事を参考にしました。

// 次月0日 → 前月末日
var endDay = new Date(time[0], nextMon, 0);
endDay = endDay.getDate();	//末日数値を代入

18~19行目までが月末日関係の処理ですが、要は1ヶ月後の同日が存在しなかった時に、実在末日まで切り下げるというものですね。(1月31日の1ヶ月後 → 2月31日?違う! → 2月28日に修正)

var day = eval(time[2]);	//設定日を数値化して代入
if(day > endDay) day = endDay; //翌月設定日 > 翌月末日

こうして求めた「期限切れの日の時刻」と「現在時刻」。比べて前者が多い(つまり期限内)の時、括弧内を実行します。22行目の括弧内は<li>~</li>の中身、つまり</li>の前にNewの表示タグを入れますよ、というものです。

var expireDate = new Date(time[0], time[1], day);
if(expireDate >= currentDate){
	lists[i].innerHTML = lists[i].innerHTML + content;
}

以上です。もっとスマートにできるという際にはお知らせいただけるとありがたいです。

参考:
■一定時間で自動的に消える New マークを付ける JavaScript の jQuery 版 - かたつむりくんのWWW
http://www.tinybeans.net/blog/2008/09/25-062812.html
■小粋空間: 新着エントリーのあるカテゴリーに New マークをつける
http://www.koikikukan.com/archives/2006/02/20-235151.php
■JavaScript による日付・時刻・時間の計算・演算のまとめ - hoge256ブログ
http://www.hoge256.net/2007/08/64.html nヶ月後、nヶ月前の日付を求める