Furudateのブログ

プログラミングやネットワーク系の知識・技術がメインのブログ。技術メモ帳的な感じになるかと。岩手から発信していきます。

【JavaScript】【PHP】jQueryでフォームダイアログを表示して裏でmysql処理

こんにちは。

今回は、jQueryでフォームをダイアログで出すこと、そして、ダイアログのボタンを押したら裏でPHPを用いてmysql処理をし、ログイン処理を行いたいと思います。

これがあるとログインフォームなどの簡単なフォームを作らなくていいし、画面も無駄に増えなくていい感じです。

jQueryを用いたダイアログ表示

まずはダイアログを表示させたいと思います。今回はフォーム付きのダイアログです。
まず、以下のものを用意します。

今回jQuery UIを使いますが、ダウンロードファイルには、デモページが同封されていますので、そこのソースコードを参考にしましょう!ダイアログ以外にも色々とあります。

また、jQuery UIのファイルについては、"jquery-ui-1.10.3.custom/js/jquery-ui-1.10.3.custom.js", "jquery-ui-1.10.3.custom/development-bundle/themes/base/" の中にあるCSSファイル、そしてimagesディレクトリの中の画像を使うことになります。

CSSファイルについてはめんどくさかったら全部自分のサーバに入れてもらって構いません。
ただ、今回のようにダイアログの機能のみを使いたい場合は、不要なファイルが多くなります。
この場合は、関連付けられているファイルのみを自分のサーバに入れたほうが良いのですが、色々と関連付けられているためぶっちゃけめんどくさいです。
そこで、"jquery-ui-1.10.3.custom/css/ui-lightness" にある jquery-ui-1.10.3.custom.css(あるいはminのほう)を使うと、ひとつのCSSファイルでダイアログ機能を使うことが出来ます。
この場合は、imagesディレクトリはcssファイルと同じ場所にあるものを使って下さい。

よって、最低限必要なファイルは以下の通りになります。

  • jqueryファイル
  • jquery-ui-1.10.3.custom.js(またはminのほう)
  • jquery-ui-1.10.3.custom.css(またはminのほう)
  • 上のCSSファイルと同じディレクトリにあるimagesの中の画像ファイル

なお、上記のCSSファイルを使うとデモページと若干ダイアログのレイアウトが異なります。
デモページのレイアウトを使いたい場合は、baseの中にあるCSSファイル全てと、同じ場所にあるimagesの中にある画像ファイルをサーバにインポートすると良いと思います。

本記事では、jquery-ui-1.10.3.custom.cssをインポートしてやっていきたいと思います。


さて、準備が整ったところでいよいよコードを書いていきます。
今回はhtmlファイルに全て書いていきます。
メールアドレスとパスワードを入力するダイアログを表示していきたいと思います。
また、バリデーションもやりたいと思います。

<!doctype html>
<html lang="jp">
<head>
	<meta charset="utf-8">
	<title>jQuery UI Dialog</title>
	
	<script src="./js/jquery-1.10.2.min.js"></script>
	<script src="./js/jquery-ui-1.10.3.custom.min.js"></script>
	<link rel="stylesheet" href="./css/jquery-ui-1.10.3.custom.min.css" type="text/css" />
	
	<style>
		.ui-dialog { font-size: 62.5%; }
		label, input { display:block; }
		input.text { margin-bottom:12px; width:95%; padding: .4em; }
		fieldset { padding:0; border:0; margin-top:25px; }
	</style>
	<script>
	$(function() {
		<!-- ダイアログのフォーム要素取得 -->
		var email = $( "#email" ),
		password = $( "#password" ),
		confPassword = $("#confPassword"),
		allFields = $( [] ).add( email ).add( password ).add(confPassword),
		tips = $( ".validateTips" );

		function updateTips( t ) {
			tips
				.text( t )
				.addClass( "ui-state-highlight" );
			setTimeout(function() {
				tips.removeClass( "ui-state-highlight", 1500 );
			}, 500 );
		}
		// 文字の長さ指定
		// @param o フォーム要素
		// @param n フォーム名
		function checkLength( o, n, min, max ) {
			if ( o.val().length > max || o.val().length < min ) {
				o.addClass( "ui-state-error" );
				updateTips(n + "は" +
					min + "文字以上" + max + "文字以下で入力してください。" );
				return false;
			} else {
				return true;
			}
		}
		// 文字パターン
		function checkRegexp( o, regexp, n ) {
			if ( !( regexp.test( o.val() ) ) ) {
				o.addClass( "ui-state-error" );
				updateTips( n );
				return false;
			} else {
				return true;
			}
		}
		
		// パスワード確認
		function checkPassword(){
			if (password.val() === confPassword.val())
				return true;
			else{
				confPassword.addClass("ui-state-error");
				updateTips("パスワードが一致していません。再度入力してください");
				return false;
			}
		}
		
		// ここでダイアログのオプション指定
		$( "#dialog-form" ).dialog({
			autoOpen: false, // trueにすると画面がロードされた時に自動でダイアログがオープンされます。
			height: 300, // 大きさ指定
			width: 350,
			modal: true, // モーダルダイアログ(ダイアログが開いている間は他の操作が出来ない)指定
			show: "explode", // 開く時と閉じるときのアニメーション指定です。
			hide: "explode",
			buttons: { // ここで各ボタンを押した時の処理を書きます。
				"パスワード再発行": function() {
					var bValid = true;
					allFields.removeClass( "ui-state-error" );
					// 文字の長さをバリデーション
					bValid = bValid && checkLength( email, "email", 6, 80 );
					bValid = bValid && checkLength( password, "password", 5, 16 );
					bValid = bValid && checkPassword();
					
					// E-mailの文字パターンバリデーション
					bValid = bValid && checkRegexp( email, /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, "入力内容が正しくありません。 例:hoge@hoge.com" );

					// バリデートが通った時の処理
					if ( bValid ) {
						alert("OK");
						$( this ).dialog( "close" );
					}
				},
				"キャンセル": function() {
					$( this ).dialog( "close" );
				}
			},
			close: function() {
				allFields.val( "" ).removeClass( "ui-state-error" );
			}
		});
		// ここでボタンを押した時にダイアログをOPENにしています
		$( "#create-user" )
			.button()
			.click(function() {
				$( "#dialog-form" ).dialog( "open" );
		});
	});
	</script>
</head>
<body>

<div id="dialog-form" title="メールアドレス登録">
	<p class="validateTips">全てが必須項目です</p>

	<form>
	<fieldset>
		<label for="email">Email</label>
		<input type="text" name="email" id="email" value="" class="text ui-widget-content ui-corner-all" />
		<label for="password">Password</label>
		<input type="password" name="password" id="password" value="" class="text ui-widget-content ui-corner-all" />
		<label for="confPassword">confPassword</label>
		<input type="Password" name="confPassword" id="confPassword" class="text ui-widget-content ui-corner-all" />
	</fieldset>
	</form>
</div>

<button id="create-user">Create new user</button>
</body>
</html>


解説はソースコードのコメントにできる限り書きました。
これを実行し、ボタンを押すとこのようなダイアログが出ると思います。

f:id:Furu222:20131009021233p:plain

ちゃんとバリデーションもできています。

f:id:Furu222:20131009021247p:plain


このダイアログの詳しい使い方やサンプルはこちらに色々と書いてありますので、参照してみて下さい。


ダイアログを表示して裏でmysql処理

次に上記のjQueryPHPを連携させて、HTML側から送られたPOSTの値を使ったログイン機能を実装したいと思います。

今回は、DBには既にハッシュ化されたパスワードがあるものとします。
また、ダイアログはパスワードを記入するのみの簡単なフォームにします。

それではコードを書いていきましょう。


test.php(メイン画面です)

<?php session_start() ?>
<!doctype html>
<html lang="jp">
<head>
	<meta charset="utf-8">
	<title>jQuery UI Dialog</title>
	
	<script src="./js/jquery-1.10.2.min.js"></script>
	<script src="./js/jquery-ui-1.10.3.custom.min.js"></script>
	<link rel="stylesheet" href="./css/jquery-ui-1.10.3.custom.min.css" type="text/css" />
	
	<style>
		.ui-dialog { font-size: 62.5%; }
		label, input { display:block; }
		input.text { margin-bottom:12px; width:95%; padding: .4em; }
		fieldset { padding:0; border:0; margin-top:25px; }
	</style>
	<script>
	$(function() {
		<!-- ログインダイアログ -->
		password = $( "#password" ),
		allFields = $( [] ).add( password ),
		tips = $( ".validateTips" );

		function updateTips( t ) {
			tips
				.text( t )
				.addClass( "ui-state-highlight" );
			setTimeout(function() {
				tips.removeClass( "ui-state-highlight", 1500 );
			}, 500 );
		}
		// 文字の長さ指定
		function checkLength() {
			if (password.val().length == "") {
				password.addClass( "ui-state-error" );
				updateTips("何も入力されていません。パスワードを入力してください。");
				return false;
			} else {
				return true;
			}
		}

		$( "#dialog-form" ).dialog({
			autoOpen: false,
			height: 300,
			width: 350,
			modal: true,
			show: "explode",
			hide: "explode",
			buttons: {
				"ログイン": function() {
					var bValid = true;
					allFields.removeClass( "ui-state-error" );
					// 文字の長さをバリデーション
					bValid = bValid && checkLength();

					// バリデートが通った時の処理
					if ( bValid ) {
						// PHPファイルにPOST
						$.post("login.php", {"password":password.val()}, function(data, status){
							if (data === "OK"){
								location.reload();
								$( this ).dialog( "close" ); // ダイアログを閉じる
							}else if (data === "NG"){ // パスワードが違うとき
								password.addClass( "ui-state-error" );
								updateTips("パスワードが違います。再入力してください。");
							}else{
								password.addClass( "ui-state-error" );
								updateTips("エラーが発生しました。もう一度入力してみてください。");
							}
						}, "text");
					}
				},
				"キャンセル": function() {
					$( this ).dialog( "close" );
				}
			},
			close: function() {
				allFields.val( "" ).removeClass( "ui-state-error" );
			}
		});

		$( "#login" )
			.button()
			.click(function() {
				$( "#dialog-form" ).dialog( "open" );
		});
		
		// ログアウト用関数
		$("#logout-dialog").dialog({
			autoOpen: false,
			height: 300,
			width: 350,
			modal: true,
			show: "explode",
			hide: "explode",
			buttons: {
					"ログアウト": function(){
					$.post("logout.php", {"logout":true}, function(){
						location.reload();
						$( this ).dialog( "close" ); // ダイアログを閉じる
					});
				},
				"キャンセル": function(){
					jQuery(this).dialog("close");
				}
			},
		});
		
		$("#logout").button().click(function(){
			$("#logout-dialog").dialog("open");
		});
	});
	</script>
</head>
<body>
<?php 
	if (isset($_SESSION["auth"])){
?>
		<div id="logout-dialog" title="ログアウト確認">
			<p>ログアウトしてもよろしいですか?</p>
		</div>
		<button id="logout">ログアウト</button>
		<p id="loginMessage">※ログイン中です</p>
	<?php }else{ ?>
		<div id="dialog-form" title="ログインフォーム">
			<p class="validateTips">パスワードを入力して下さい</p>

			<form>
			<fieldset>
				<label for="password">パスワード</label>
				<input type="password" name="password" id="password" value="" class="text ui-widget-content ui-corner-all" />
			</fieldset>
			</form>
		</div>
		<button id="login">ログイン</button>
		<p id="loginMessage">※ログアウト中です</p>
	<?php }	?>
</body>
</html>


この画面はログインする前にはログインボタン、ログイン後はログアウトボタンを表示するシンプルな画面です。
$.POSTでlogin.phpにPOSTを送信し、ログイン成功の場合はリロードをしています。
jQuery.postの使い方はこちらに書いてあります。

ログアウトに関しては、logout.phpにPOST送信をし、logout.php側でセッション破棄してログアウト処理を行っています。


ログイン処理のPHPファイルです。

login.php

<?php
	// POSTない場合はリダイレクト
	if (!isset($_POST["password"])){
		header("Location: ./sample.php");
	}
	
	$mysqli = new mysqli("localhost", "username", "password", "database");
	
	if (mysqli_connect_errno()){
		printf("Connect failed: %s\n", mysqli_connect_error());
		exit();
	}
	
	$mysqli->set_charset("utf-8");
	
	$pass = sha1($_POST["password"]);
	$query = "select * from index_login where password = ?;";
	
	if ($stmt = $mysqli->prepare($query)){
		$res = ""; // リターン用変数
		$stmt->bind_param('s', $pass);
		if ($stmt->execute()){
			$stmt->store_result();
			if ($stmt->num_rows > 0){
				$res = "OK";
				session_start();
				$_SESSION["auth"] = true;
			}else
				$res = "NG";
		}else{
			$res = "error";
		}
	}else
		$res = "error";
					
	$stmt->close();
	$mysqli->close();
	
	echo $res;
?>

こちらは特に難しいことはしていません。mysqliでハッシュ化したPOSTの値を使ってDBを参照し、num_rows()でヒット件数を確認しています。
ヒットした場合(パスワードが一致した場合)はSESSIONに値を代入し、OKという文字列をsample.php側に返しています。


次にログアウト処理です。

<?php
	// POSTない場合はリダイレクト
	if (!isset($_POST["logout"])){
		header("Location: ./sample.php");
	}
	session_start();
	
	$_SESSION = array();
	session_destroy();
?>

こちらはセッション破棄しているだけなので特に何もありませんね。

実行結果がこちらになります。

まずはログイン前

f:id:Furu222:20131009024156p:plain

ログイン後

f:id:Furu222:20131009024247p:plain



長くなってしまいましたが、以上です。

jQueryすごく便利だなと改めて実感しました。

それでは。

2013/10/11 追記

このダイアログを使って、OKボタンが押された時に別のダイアログを出そうとして以下のようなコードを書いたらエラーが出ました。

$( "#dialog" ).dialog({
   autoOpen: false,
   modal: true,
   buttons: {
      "OK": function(){
      $("#new-dialog").dialog("open");
      $(this).dialog( "close" );
      }
   }
});

エラー文

"Uncaught cannot call methods on dialog prior to initialization; attempted to call method 'close'"


これに関してはこちらの情報が参考になりました。

英語なのでよくわかりませんが、とりあえず何回も$(this).dialog("close")をやるのはよろしくないようで、thisではなく、ちゃんと名前で指定してあげる必要があるみたいです。

なので、以下のようにすると解決出来ます。

$( "#dialog" ).dialog({
   autoOpen: false,
   modal: true,
   buttons: {
      "OK": function(){
      $("#new-dialog").dialog("open");
      $("#dialog").dialog( "close" ); // 名前で指定
      }
   }
});

同じようなエラーが出た人は是非確認してみてください!


【HTML】【CSS】フォームでEnterを押しても送信しないようにする

こんにちは。

HTMLで少し詰まったことがあったのでメモしておきます。

テキストフォームにフォーカスがある状態でEnterを押すと、ボタンを押さなくてもフォームの内容が送信されてしまいます。
場合によっては便利ですが、この機能を無効にしたい場合もあります。

※ちなみに、この挙動はブラウザによって違うようです。
Chromeの場合はテキストフォームがひとつのときは送信されましたが、複数あると送信されませんでした。
こちらにブラウザごとの挙動がまとめられています。


テキストフォームとボタンがあるフォームの場合は、以下のようにして解決することが出来ます。

<form action="hoge">
  <input type="text" name="testa"> <input type="text" name="testb">
  <button type="button" onClick="submit();">送信</button>
</form>

input type=submit を使うのではなく、buttonタグにしてonClickでsubmit()としています。

この方法についてはググると色々なところに出ています。
ただ、自分の場合はボタンがなく、テキストフォームがひとつだけの状態のときに、この機能を無効にしたかったのです。


その方法は以下のようになります。

<form action="hoge">
  <input type="text" name="testa">
  <input type="text" name="dummy" style="position:absolute;visibility:hidden">
  <input type="button" value="hoge" onClick="submit();">
</form>

これはCSSを用いて見えないテキストフォームを作っています。
Enterを押すと送信されてしまうのは、テキストフォームがひとつの場合(少なくともChromeでは)でしたので、これで解決できました。
ちなみに、この場合はinput type=submit でもいけます。


今回は以外なところで詰まってしまいました。

以上です。
それでは。

【CSS】ボックス内で改行する

こんにちは。

divタグなどを使ってボックスを作った場合、そこに長い文字列が入ってしまうと通常ははみ出して表示されてしまいます。

CSSで "overflow: scroll" などを使えばスクロールはできますが、改行することは出来ません。

今回はそのやり方をメモしておきます。

参考サイトに詳しく載ってありましたが、divタグを使う場合はCSSで "word-wrap: break-word;" を使うと良いみたいです。


今回は簡単ですが以上です。

それでは。


【Javascript】【PHP】jQueryでajax的なコメント書き込み処理

こんばんは。

今回はjQueryを使って画面遷移なしでajax的にコメントの投稿と表示をしてみたいと思います。

こちらのサイトがとても詳しく書いてあり、非常に参考になりました。

基本的には前のエントリで書いた$.postを用いて実装しています。


DBのテーブルを用意

まずはコメントを格納するテーブルを用意します。
今回は投稿者名(ユーザ名)とコメント内容、投稿日のカラムを用意します。

PHP処理

次にPHP側です。
PHP側ではcomment_send.phpとcomment_list.phpを作りました。

comment_send.phpでは、POSTで与えられた内容をテーブルに格納し、成功したら"OK"、失敗したら"NG"を返す処理にしています。

comment_list.phpでは、コメントの読み込みをしています。読み込みに成功したらコメントリストを表示するHTMLを返し、コメントがない場合は"No Comment"、エラーの場合は"NG"を返すようにしています。

HTML

次にHTML側です。

<div id="comment-list"></div>
<div id="comment-input">
	<div id="input-names">
		<form>
			<label>ユーザ名<br /><input type="text" name="user_name" placeholder="ユーザ名" /></label><br /><br />
			<label>内容<br /><textarea name="message" rows = "7" cols = "35"></textarea></label>
			<input type="button" id="send-comment" value="投稿" />
			<span id="send-comment-error"></span>
		</form>
	</div>
	<div id="input-thanks">コメントが投稿されました。</div>
</div>


各div(span)タグの役割は以下の通りです。

  • comment-list
    • コメントリストが表示される領域です。
  • input-names
    • フォーム領域です。
  • send-comment-error
    • エラーを表示する領域です。
  • input-thanks
    • 投稿が成功したときに表示する領域です(デフォルトではCSSで display:none として非表示にしています)

JavaScript

次にjQueryのほうです。

// コメント投稿ボタンを押したらsendComment()を実行
$("#send-comment").click(function(){
	if (confirm("投稿しますか?")) // OKボタンが押されたとき
		sendComment();
});

// 既存コメント読み込んで表示
getComment();

// コメント送信
function sendComment(){
	$("#send-comment-error").text("");
	if (!$("textarea[name=message]").val()){
		$("#send-comment-error").text("コメントを入力してください。");
		return false;
	}
	var user_name; // ユーザ名格納変数
	if (!$("input[name=user_name]").val())
		user_name = "名無し";
	else
		user_name = $("input[name=user_name]").val();
	
	$.post("comment_send.php", {"user_name":user_name, "message": $("textarea[name=message]").val()}, function(data){
		// 書き込み完了したらMessageを出力しコメント一覧を読み込む
		if (data === "NG"){ // コメントの投稿に失敗した場合
			$("#send-comment-error").text("コメントの投稿に失敗しました。もう一度やってみてください。");
			return false;
		}else{ // コメント投稿に成功した場合
			getComment();
			$("#input-thanks").show("slow");
		}
	});
}

// コメント一覧表示
function getComment(){
	$.post("comment_list.php", null, function(data){
		if (data === "NG"){
			$("#send-comment-error").text("コメントの読み込みに失敗しました。リロードしてみて下さい。");
			return false;
		}else if (data === "No Comment")
			$("#comment-list").text("コメントがありません。");
		else{
			$("#comment-list").html(data);
		}
	});
}


最初にconfirmで確認ダイアログを出しています。
また、ユーザ名が入力されていない場合は「名無し」としてDBに格納します。
getComment()では、コメントリストのHTMLが帰ってくるので、jQuery.htmlを使って直接HTMLソースを追記しています。


以上です。
やはりコメント投稿はajaxだと嬉しいですよね。


それでは。