WEBアプリケーションを作ってみました

はじめまして!大阪工業大学の藪野好一です。
ICTトラブルシューティングコンテストは初参加となります。普段は、大学でルーターやスイッチを触ってネットワークの勉強をしています。トラブルシューティングコンテストでは、問題作成の担当をしております。問題は、大会当日の楽しみとして頂いて、今回はWEBアプリケーションについて書きたいと思います。

はじめに

WEBページは、ページを見る人の端末(クライアント側)で処理したページとサーバー側で処理を行った結果を表示するページそしてそれらを組み合わせたページがあります。今回は、クライアント側で動くWEBアプリケーションを例にWEB技術の紹介をしたいと思います。

WEBアプリケーションで使われている技術例としてHTML、CSS、JavaScriptがあります。
HTML、CSS、JavaScriptとは何かをざっくり言うと、

  • HTMLはページの大まかな構成を定義するもの
  • CSSはページのデザインの調整をするもの
  • JavaScriptはページに動きをつけるもの

となります。

例えてみると

ここで、これらを家に例えてみると以下のようなイメージとなると思います。

  • HTMLは、設計図や基礎のようなもの
  • CSSは、壁の材料や家のデザイン
  • JavaScriptは、電化製品や家具のようなもの

これらのクライアント側技術を組み合わせて、クライアント側で動くWEBアプリケーションを作ることが出来ます。WEBアプリケーションは、CSSやJavaScriptに対応したブラウザがあれば、どのような端末からでも使うことが出来るので、大きな可能性のある技術だと私は考えています。指定した日付に変わるまでの時間をカウントダウンするWEBアプリケーションを作成したのでそのソースコードを使って紹介したいと思います。

プログラム例

time-counter-screen

HTML

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-type" content="text/html; charset=UTF-8">
 <title>カウントダウン</title>
 <script src="js/jquery-1.11.3.js"></script>
 <link rel="stylesheet" type="text/css" href="css/style.css">
 <script src="js/timecounter.js" type="text/javascript"></script>
 <link rel="shortcut icon" href="favicon.ico" />
</head>
<body>
 <div id="clock"></div>
 <div id="printArea"></div>
 <div id="searchArea"></div>
</body>
</html>

このように、HTMLのヘッダーには、ページに関する情報のほか読み込みたいCSSやJavaScriptの情報を記述します。
bodyには、どこに、どのような要素を置くという情報を記述します。一般的なWEBページにある、タイトルや見出し、リンク、リストといった要素はここに書きます。
今回作成したWEBアプリケケーションは、時計を表示する場所、日数を表示する場所、調べたい日付を入力する場所の3つの場所をHTMLファイルで定義しました。

ファビコン(favicon.ico)は、ブラウザでページを表示した際にタイトルの横に出てくるアイコンのことです。画像からファビコンを作成することが出来るサイトもあります。

CSS(style.css)

@charset "utf-8";

table {
	border-collapse: separate;
	border-spacing: 15px 5px;
	margin: 0 auto;
}

th{
	background-color:#00A0E9;
	color: #FFF;
	margin: 10px;
	padding:5px;
	border-top-left-radius: 10px;
	border-top-right-radius: 10px;
	-webkit-border-top-left-radius: 10px;
	-webkit-border-top-right-radius: 10px;
	-moz-border-radius-topleft: 10px;
	-moz-border-radius-topright: 10px;
	font-size: 1.7em;
}
td{
	text-align:center;
	background-color:#EEEEEE;
	border-bottom-left-radius: 10px;
	border-bottom-right-radius: 10px;
	-webkit-border-bottom-left-radius: 10px;
	-webkit-border-bottom-right-radius: 10px;
	-moz-border-radius-bottomleft: 10px;
	-moz-border-radius-bottomright: 10px;
	font-size: 1.8em;
}

#clock{
	font-size:4em;
	width:1080px;
	margin: 0 auto;
	padding : 10px;
}
#printArea {
	font-size:4em;
}
.printClock{
	width:200px;
	float:left;
	height:60px;
	background:#123456;
	color:#FFF;
	text-align:center;
	vertical-align: middle;
	border-radius: 10px;
	-moz-border-radius: 10px;
	-webkit-border-radius: 10px;
	margin: 15px auto;
}
.clockSpace{
	width:15px;
	height:40px;
	vertical-align: middle;
	float:left;
	padding:0 5px;
}
.clearArea{
	clear:both;
	vertical-align: middle;
}

#searchArea{
	text-align : center ;
	padding:15px;
}

button {
	margin: 0 0 0 20px;
	font-size: 1.4em;
	font-weight: bold;
	padding: 10px 30px;
	color: #fff;
	border-style: none;
	border-radius: 5px;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	background:#064583;
	transition: all 1s;
}

button:hover {
	opacity: 0.8;
}

input{
	width:300px;
	height:60px;
	font-size:2em;
}

#searchArea{
	font-size:1.4em;
}

body{
	animation: bg-color 60s infinite;
}


@keyframes bg-color {
	0% { background-color: #B3E5FC; }
	10% { background-color: #B2DFDB; }
	20% { background-color: #C8E6C9; }
	30% { background-color: #F0F4C3; }
	40% { background-color: #FFF9C4; }
	50% { background-color: #FFECB3; }
	60% { background-color: #ffcdd2; }
	70% { background-color: #F8BBD0; }
	80% { background-color: #E1BEE7; }
	90% { background-color: #C5CAE9; }
	100% { background-color: #B3E5FC; }
}

CSSはデザインの定義を行うために書きます。タグやid、classを指定することが出来ます。

  • タグは、タグ名
  • idは、#id名
  • classは、.class名

で指定を行います。また、これらを組み合わせて、指定することも出来ます。

HTMLは入れ子で定義出来ます。

#idname .classname{
/*指定したい内容*/
}

と書くとid名がidnameの要素の中にある、class名がclassnameという要素を指定することになります。

ただし

#idname, .classname{
/*指定したい内容*/
}

と書くとid名がidnameの要素と、class名がclassnameという要素の両方を同時に指定することになるのでこれらの書き間違いには注意が必要です。

ところで、CSS3対応のブラウザなら、JavaScriptを使うことなく、アニメーションを実現することが可能なのでいろいろ調べてみると面白いかと思います。例えば、102行目から119行目では背景色を60秒の間に指定した10色で順番に色を変えるという指定をしています。とても簡単に指定することが出来るのでいろいろ試してみてください。

JavaScript(timecounter.js)

JavaScriptを用いることで、動きのあるページを作ることが出来ます。今回作成したページは、jQueryというJavaScriptのライブラリを使っています。jQueryを使用することにより簡単にプログラムを実装することが出来るため、多くのWEBサイトやWEBアプリケーションで利用されています。

https://jquery.com/

 

timecounter.jsは、大きく分けて指定した日までの残り時間を表示する関数と時計を表示する関数の2つの関数が宣言されています。それらを定期的に実行することで時間とともに表示が変化します。
main関数は、jQueryを使っているため、functionの前に$が付いています。「$」は、「jQuery」の省略記号です。

//main関数
$(function() {
		init();

		setInterval(function(){

		var now_date = new Date();
		var now_year = now_date.getFullYear();
		var now_month = now_date.getMonth()+1;
		var now_day = now_date.getDate();

		var date = $(":input[name='search_date']").val();
		target_year = parseInt(date.substring(0,4));
		target_month = parseInt(date.substring(5,7));
		target_day= parseInt(date.substring(8,10));

		var remain_day = remainCount(now_year, now_month, now_day, target_year, target_month, target_day);

		$("#printArea").empty();
		var table = $('<table>');

		var th = $('<tr>')
			.append('<th colspan="4">' + target_year +"年" + target_month + "月"+ target_day +"日まで"+'</th>');

		table.append($('<thead>').append(th));

		var tbody = $('<tbody>');
		table.append(tbody);

		if(remain_day > 0){
			var td = $('<tr>')
				.append('<td>' + (remain_day-1) + '日' + '</td>')
				.append('<td>' + sToh(todayRemainSecond()) + '時間' + '</td>')
				.append('<td>' + sTom(todayRemainSecond()) + '分' + '</td>')
				.append('<td>' + sTos(todayRemainSecond()) +'秒</td>');
			}else{
				td = $('<tr>')
					.append('<td>エラー</td>')
					.append('<td></td>')
					.append('<td>計算</td>')
					.append('<td>出来ません</td>');
			}

		table.append(td);
		$("#printArea").append(table);
	  },1000);

		$(function() {
			$("#searchArea").on("click","#notPrint",function(){
				$("#searchArea").css("display","none");
			});
		});
		$('#printArea').hover(function(){
			var date = $(":input[name='search_date']").val();

			var p = $('<p>')
				.append('<input type="date" name="search_date" size="4" value="' + date + '">')
				.append('<button id="notPrint">閉じる</button>');
			$('#searchArea').empty();
			$('#searchArea').append(p);
			$("#searchArea").css("display","block");

		});
});

//初期設定(フォームに次の日の日付をセットする)
function init(){
	$(function(){

		var tomorrow = findTomorrow();

		var p = $('<p>')
				.append('<input type="date" name="search_date" size="4" value="' + tomorrow + '">')
				.append('<button id="notPrint">閉じる</button>');
		$('#searchArea').append(p);
	});
}

//うるう年かどうかを判定
function isLeapYear(year){
	if(year % 4 == 0){
		if(year % 100 == 0 && year % 400 != 0){
			return false;
		}else{
			return true;
		}
	}else{
		return false;
	}
}

//指定した月の日付を求める
function monthDay(month, year){
	if(month == 2){
		if(isLeapYear(year)){
			return 29;
		}else{
			return 28;
		}
	}else if(month == 4 || month == 6 || month == 9 || month == 11){
		return 30;
	}else{
		return 31;
	}
}

//n_yearの次の年の1月1日からt_yearの前の年の12/31までの日数を返却
function addYearDay(n_year, t_year){
	var i = n_year + 1;
	var total = 0;

	//指定した年までの年数分365を足す
	while(i<t_year){
		if(isLeapYear(i)){
			total++;
		}
		total += 365;
		i++
	}
	return total;
}

//指定した日までの日数をカウント(年をまたぐ指定は出来ない)
function counter(year,n_month,n_day,t_month,t_day){
	var count= 0;
	var i = n_month + 1;
	while(i< t_month){
		count += monthDay(i,year);
		i++;
	}
	if(n_month == t_month){
		count -= monthDay(n_month,year);
	}
	count = count + remainDay(n_day, monthDay(n_month, year)) + t_day;
	return count;
}

//指定した日から指定した日までの日数を数える 過去を指定した場合は-1を返す
function remainCount(now_year, now_month, now_day, target_year, target_month, target_day) {
	if(!dec(now_year, now_month, now_day, target_year, target_month, target_day)){
		return -1;
	}
	var count = 0;
	count += addYearDay(now_year, target_year);
	var diff = target_year - now_year;

	if(diff == 0){
		count = counter(now_year, now_month, now_day, target_month, target_day);
	}else{
		//今年の残り日数と指定した年の指定した日までの日数をカウントする
		count += counter(now_year, now_month, now_day, 12, 31);
		count += counter(target_year, 1, 1, target_month, target_day);
	}
	count--;

	return count;
}

//過去かどうか判定
function dec(now_year, now_month, now_day, target_year, target_month, target_day){
	if((now_year * 10000 + now_month * 100 + now_day) > (target_year * 10000 + target_month * 100 + target_day)){
		return false;
	}else{
		return true;
	}
}

//指定した日までの日数を求める。年や月をまたぐ場合使用できない
function remainDay(n_day, t_day){
	var rem =  t_day - n_day + 1;
	return rem;
}

//1から9の一桁の数字を0X形式に変換
function toTime(int_num) {
    var ret;
    if (int_num < 10) {
        ret = "0" + int_num;
    }else {
        ret = int_num;
    }
    return ret;
}
//指定された日数と今日の残り時間を秒に変換して返却
function secondCount(today_remain_second, remain_day){
	var oneday = 86400;//60*60*24
	return today_remain_second + (remain_day -1) * oneday;
}

//今日の残り時間を秒で返却
function todayRemainSecond(){
	var date = new Date();
	var n_hour = date.getHours();
	var n_minute = date.getMinutes();
	var n_second = date.getSeconds();

	var oneday = 86400;//60*60*24

	var pass_time = n_hour * 3600 + n_minute * 60 + n_second;
	return oneday - pass_time;
}

//秒を時間に変換(分と秒を切り捨て)、
function sToh(second){
	return toTime(Math.floor(second/3600));
}

//秒を時間に変換したあまりの時間を分に変換(秒を切り捨て)、
function sTom(second){
		return toTime(Math.floor(second % 3600 / 60));
}

//分を時間に変換したあまりの時間を秒に変換、
function sTos(second){
	return toTime(Math.floor(second % 3600 % 60));
}

//明日の日付をXXXX-YY-ZZ形式で返却
function findTomorrow(){
	var date = new Date();
	var year = date.getFullYear();
	var month = date.getMonth()+1;
	var day = date.getDate();

	return findNextDay(year, month, day);
}

//指定された次の日をXXXX-YY-ZZ形式で返却
function findNextDay(year, month, day){
	//dayを1日増やし、その月の最終日をXXXX-YY-ZZ形式で返却
	if(++day > monthDay(month, year)){
			month++;
			day = 1;
			if(month == 13){
				year++;
				month = 1;
			}
	}
	return "" + year +  "-" +toTime(month) + "-" + toTime(day);
}

//時計を表示
function clock() {
    var t_date = new Date();
    var month = t_date.getMonth()+1;
    var day = t_date.getDate();
    var year = t_date.getFullYear();

    var n_hour = t_date.getHours();
    var n_minute = t_date.getMinutes();
    var n_second = t_date.getSeconds();
    var n_day = t_date.getDay();
    var week = ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"];

   var p_date = "<div class ='printClock'>"
		+ year + "年</div>"
		+ "<div class='clockSpace'></div>"
		+ "<div class ='printClock'>"
		+ toTime(month) + "月</div><div class='clockSpace'> </div>"
		+ "<div class ='printClock'>"
		+ toTime(day) +"日</div><div class ='clearArea'></div>";

    var time = "<div class ='printClock'>"
		+ toTime(n_hour) + "時</div>"
		+ " <div class='clockSpace'> </div> "
		+ "<div class ='printClock'>"
		+ toTime(n_minute) + "分</div>"
		+ "<div class='clockSpace'> </div>"
		+ "<div class ='printClock'>"
		+ toTime(n_second) + " 秒</div><div class='clockSpace'> </div>"
		+ "<div class ='printClock'>"+ week[n_day]
		+"</div><div class ='clearArea'></div>";
    document.getElementById("clock").innerHTML = p_date + time;


}
setInterval('clock()', 1000);

今回のカウントダウンプログラムは、

  • 何年先かをカウント
  • 1年より先なら年数分数値を足す
  • 今年の残りの日付を計算
  • 指定された年の指定した日までの日付を計算
  • これらを合計する
  • 求めた値を整えて表示

という処理手順で実行します。

その処理をsetInterval関数で1000ミリ秒に一度実行することより自動的にカウントダウンが行われるようにしています。1ミリ秒とは0.001秒のことです。従って、1000ミリ秒は1秒となります。

//関数名:kansu 実行間隔1000ミリ秒
setInterval('kansu()', 1000);

jQueryの場合は、関数を引数に取るので次のように書きます。
function()は、名前のない関数で無名関数と呼ばれています。

//jQueryの場合
$(function(){
	setInterval(function(){
        	//ここに処理を書く
	},1000);
});

jQueryはcssの操作も簡単に出来ます。作成したプログラムでも、jQueryを使ってCSSを操作し、検索ボックスの表示、非表示を切り替えています。以下のように指定することにより実装出来ます。なお、セレクタはCSS同様にHTMLタグやHTMLで定義したID名やクラス名で指定します。

	$("セレクタ").css("display","none");
	//セレクタで指定した部分を非表示にする。

	$("セレクタ").css("display","block");
	//セレクタで指定した部分を表示にする

jQueryのマウスイベント

マウスのクリックや、マウスがある要素の上に乗ったことをきっかけとして処理を行うことをマウスイベントと言います。今回マウスクリックの例を紹介したいと思います。

	//クリックしたときに関数を実行
	$(function() {
		$("セレクタ").click(function(){
			//実行する処理
		});
	});

のように書けば、セレクタ名で指定した場所がクリックされれば関数が実行されます。しかし、JavaScriptでHTMLのソースを作って要素の追加を行った場合、この方法では上手く動作しません。そこで、次のように記述します。

	$(function() {
		//セレクタ1 HTMLコードを追加したセレクタ
		//セレクタ2 クリックを認識してほしいセレクタ
		$("セレクタ1").on("click","セレクタ2",function(){			
			//実行する処理
		});
	});

この方法なら、動的に追加された要素であっても正しく動作すると思います。

サンプルプログラムの使い方

HTMLヘッダーのようにファイルを配置すれば、プログラムを使って頂けるかと思います。
jQueryに関しては以下のページからダウンロードするか、ヘッダーでjQueryを読み込んでいる部分を

	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>

に書き換えて実行してみてください。

 

http://jquery.com/download/

 

画像のファイル配置と同じ配置にすれば動くはずです。

filepath

syntaxhighlighter

syntaxhighlighterとは、プログラムのソースコードをWEBページ上でテキストエディターのようにきれいに表示してくれるプラグインのことです。
このブログや、解説サイトで使われているのでご存知の方もいらっしゃるかもしれませんが紹介だけしたいと思います。
簡単に導入でき、ソースコードが読みやすくなるので是非導入してみてください!

http://alexgorbatchev.com/SyntaxHighlighter/

 

最後に

JavaScriptのプログラムは急いで作成したのでバグが含まれている可能性があります。バグを発見したり、もっとコードをきれいに書く方法があれば、教えて頂けると嬉しいです。それでは、大会本番でお会いしましょう!