[PHP, JS]jQuery UI autocompleteを使った自動補完(候補予測)

キーワード検索を行う際、入力欄に文字の一部を入力すると当てはまる単語をリストアップしてくれるオートコンプリート(自動補完)機能は便利です。
jQuery UI には Autocomplete というウィジェットが用意されており、それを使えば簡単に実現できます。
あらかじめ単語が決まっていて、数が少ないのであればそのまま Javascript 内に単語一覧を書いてしまえばいいのですが、MySQL などのデータベースに登録されている情報を元に候補一覧を表示したい場合、Ajax を利用して PHP などから動的に取得する必要があります。

form.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Auto Complete</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/themes/smoothness/jquery-ui.css" />
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>
<script>
$(function() {
	$("#sample").autocomplete({
		source: "./autocomplete.php"
	});
});
</script>
</head>
<body>

<form>
<input type="text" id="sample" />
</form>

</body>
</html>

autocomplete.php

<?php
// 単語のリスト
$list = array(
	'Apple',
	'Orange',
	'Grape',
	'Banana',
	'Pear',
	'Melon',
	'Peach',
	'Lemon'
);

$words = array();

// 現在入力中の文字を取得
$term = (isset($_GET['term']) && is_string($_GET['term'])) ? $_GET['term'] : '';

// 部分一致で検索
foreach($list as $word){
	if(mb_stripos( $word, $term) !== FALSE){
		$words[] = $word;
	}	
}

header("Content-Type: application/json; charset=utf-8");
echo json_encode($words);

必要となるのはフォームの記述されたファイルと Ajax 処理を行う PHP ファイルです。
データの受け渡しは JSON で行われ、テキストボックスに現在入力中の文字は通常 term に格納されて渡されます。
サンプルでは単純に固定の単語一覧を JSON に変換して表示していますが、データベースから取得する場合は WHERE ~ LIKE で絞り込んだ単語一覧を JSON に変換します。データベースへの負荷を減らすためにセッション等にキャッシュするのも良いと思います。
デフォルトでは一文字入力していれば候補を表示しますが、オプションで minLength を指定すれば最小文字数を変更できます。

$( "#sample" ).autocomplete({
    source: "./autocomplete.php",
    minLength: 3
});

参考: Autocomplete Widget
http://api.jqueryui.com/autocomplete/

[HTML5+JS+Canvas, PHP]アナログ時計の作り方

canvas_clock

Javascript と html5 の canvas を組み合わせてリアルタイムで動作するアナログ時計を作ってみました。
おまけですが PHP と GD を使ったバージョンも合わせて掲載しています。

動作サンプル

Html5 + Javascript + canvas

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Clock</title>

<script type="text/javascript">
<!--
window.onload = function()
{
	var width   = 100;
	var height  = 100;
	var centerX = Math.floor(width / 2);
	var centerY = Math.floor(height / 2);
	var canvas  = document.getElementById('clock');
	var context = canvas.getContext('2d');
	
	setInterval(drawClock, 1000);
	drawClock();
		
    function drawClock() {
		
		var date   = new Date();
		var hour   = date.getHours();
		var minute = date.getMinutes();
		var second = date.getSeconds();

		
		// 色の指定
		context.strokeStyle = '#666666';

		context.clearRect(0, 0, canvas.width, canvas.height);
		
		// 文字盤の描画
		context.beginPath();
		context.arc(centerX, centerY, centerX - 1, 0, Math.PI*2, false);
		context.stroke();
			
		context.save();
		context.translate(width/2, height/2);
		for(var i=0;i < 360; i+= 30){
			context.rotate( 30 * Math.PI / 180);
			context.beginPath();
			context.moveTo(0, centerY * 0.8);
			context.lineTo(0, centerY * 0.9);
			context.stroke();
		}
		context.translate(-width/2, -height/2);
		context.restore();
		
		context.strokeStyle = '#333333';
		
		drawHand(centerY * 0.5, hour * 30 + minute / 60 * 30);
		drawHand(centerY * 0.8, minute * 6 + second / 60 * 6);
		
		context.strokeStyle = '#EE0000';
		
		drawHand(centerY * 0.8, second * 6);
	}
	
	// 針の描画
	function drawHand(length, angle){
		context.save();
		context.translate(centerX, centerY);
		context.rotate( angle * Math.PI / 180);
		context.beginPath();
		context.moveTo(0, 0);
		context.lineTo(0, -length);
		context.stroke();
		context.restore();
	}
}
//-->
</script>

</head>
<body>
<canvas id="clock"></canvas>
</body>
</html>

rotate() だけでは回転軸を指定して回転させる事ができないので、translate() を使って画像全体をずらし、回転させた後で元の位置に戻すことで回転の中心を変えています。


PHP + GD

PHP の imagedrawellipse() と imagedrawline() の組み合わせでも一応作れます。
こちらはリアルタイム動作ではありません。また、サーバー側のリソースを消費するので使い道はあまり無いかもしれません。

clock

<?php
date_default_timezone_set('Asia/Tokyo');

$date   = new DateTime();
$hour   = (int)$date->format("h");
$minute = (int)$date->format("i");

$width  = 100;
$height = 100;

$centerX = floor($width / 2);
$centerY = floor($height / 2);

$img       = imagecreatetruecolor($width, $height);
$handColor = imagecolorallocate($img, 0x33, 0x33, 0x33);
$dialColor = imagecolorallocate($img, 0xAA, 0xAA, 0xAA);
$bg        = imagecolorallocate($img, 0xFF, 0xFF, 0xFF);

imagefill($img, 0, 0, $bg);
imagecolortransparent($img, $bg);

// 文字盤の描画
imageellipse($img, $centerX, $centerY, $width - 1, $width -1, $dialColor);

for($i=0;$i < 360;$i+=30){
	list($x1, $y1) = point_rotate($centerX * 0.8, $i);
	list($x2, $y2) = point_rotate($centerX * 0.9, $i);
	imageline($img, $x1 + $centerX, $y1 + $centerY, $x2 + $centerX, $y2 + $centerY, $dialColor);
}

// 針の描画
$hour = $hour % 12;
list($hourX, $hourY) = point_rotate($centerX * 0.5, $hour * 30 + $minute / 2);
imageline($img, $centerX, $centerY, $hourX + $centerX, $hourY + $centerY, $handColor);
list($minuteX, $minuteY) = point_rotate($centerX * 0.8, $minute * 6);
imageline($img, $centerX, $centerY, $minuteX + $centerX, $minuteY + $centerY, $handColor);

header("Content-type:image/png");
imagepng($img);
imagedestroy($img);


function point_rotate($r, $degree){
  $radian = deg2rad($degree - 90);
  $x = round($r * cos($radian));
  $y = round($r * sin($radian));
  return array($x, $y);
}

[PHP, JS]jQueryのAjaxによるPHP、Javascript間の送受信(JSON)

Ajax(非同期通信)を用いれば画面遷移なしでデータのやり取りができるため、最近では定番の技術として頻繁に用いられています。

今回は Javascript を使ってホストにデータを送信し、処理結果を受け取るというプログラムを JSON、JSONP を使うケース、同一ドメイン、クロスドメイン環境のケースに分けて説明します。

同一ドメイン間で JSON を使ったサンプル

クライアント

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Ajax</title>
        <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
        <script>
        $(function(){
            $("#button1").on('click', function(event){
                event.preventDefault();
                var param = { "text": "Hello" };

                $.ajax({
                    type: "GET",
                    url: "get.php",
                    data: param,
                    crossDomain: false,
                    dataType : "json",
                    scriptCharset: 'utf-8'
                }).done(function(data){
                    alert(data.text);
                }).fail(function(XMLHttpRequest, textStatus, errorThrown){
                    alert(errorThrown);
                });
            });
        });
        </script>
    </head>
    <body>
        <button id="button1">submit</button>
    </body>
</html>

ホスト

<?php
// Ajax以外からのアクセスを遮断
$request = isset($_SERVER['HTTP_X_REQUESTED_WITH'])
     ? strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) : '';
if($request !== 'xmlhttprequest') exit;

$text = filter_input(INPUT_GET, 'text');
header('Content-type: application/json; charset=utf-8');
echo json_encode(['text' => $text . ', World!']);

出力結果

Hello, World!

「Hello」と書かれたボタンを押すと、JavaScript から PHP に「Hello」という文字列が送信されます。
PHP はそれに「, World!」という文字列を加えて返します。
Javascript は結果を受け取り「Hello, World!」と表示する流れです。

非クロスドメイン環境で ajax を使うと HTTP_X_REQUESTED_WITH というヘッダー情報が送信されます。これをもとに Ajax 以外の方法をブロックしています。

「type: "POST"」とした場合は POST メソッドで送信されるのでホスト側も「$text = filter_input(INPUT_POST, "text");」のようにして受け取ることになります。

JSONP を使ったクロスドメイン Ajax

JSONP(JSON with padding)を使うとクロスドメイン環境での Ajax が可能です。
クライアントの dataType に jsonp を指定し、type を GET に変更します。

クライアント

<script>
$(function(){
    $("#button1").on('click', function(event){
        event.preventDefault();
        var param = { "text": "Hello" };

        $.ajax({
            type: "GET",
            url: "get-jsonp.php",
            data: param,
            crossDomain: true,
            dataType : "jsonp",
            scriptCharset: 'utf-8'
        }).done(function(data){
            alert(data.text);
        }).fail(function(XMLHttpRequest, textStatus, errorThrown){
            alert(errorThrown);
        });
    });
});
</script>

ホスト

<?php
text = filter_input(INPUT_GET, 'text');
$callback = filter_input(INPUT_GET, 'callback');
$callback = htmlspecialchars(strip_tags($callback));
 
$param = ['text' => $text . ", World!"];

header('Content-type: text/javascript; charset=utf-8');
printf("{$callback}(%s)", json_encode( $param ));

JSONP ではクライアントから callback というデータが送信されます。これを javascript の関数を呼び出すような形にして出力するとデータの受け渡しができるようになります。

JSON を使ったクロスドメイン Ajax

JSON を使ってクロスドメイン通信を行う場合は次のヘッダーをサーバー側で出力しておく必要があります。

header("Access-Control-Allow-Origin: *");

「*」はすべてのサイトからの通信を許可するということを意味します。特定のサイトからのみリソースの利用を許可する場合は次のようにして URL を指定します。

header("Access-Control-Allow-Origin: http://foo.com http://bar.com");

不特定多数のクライアントにリソースシェアリングを行う場合は認証機能をつけることについても検討して下さい。

参考:
http://www.learningjquery.com/2010/03/detecting-ajax-events-on-the-server/