Webサイト制作科 - 作品紹介

Webサイト制作科 - 作品紹介

問い合わせフォームの作成

フォームUIの課題とjQuery Mobileによる解決


 iPhone(左)の標準フォームUIとAndroid(右)の標準フォームUI

フォームUIの基本

  • jQuery Mobileのフォーム機能では、通常のHTMLと同じように、action属性とmethod属性を指定したform要素内にフォーム部品を記述していきます
<form action="form.php" method="post">
(フォームの内容)
</form>
  • フォーム部品は、data-role属性に「fieldcontain」を指定した要素で各パーツ(input要素やtextarea要素など)を包んで記述します。
お問い合わせフォームの「名前」からマークアップ
  • 「名前」は1行テキスト入力フォームですのでinput要素を使い、ラベル部分をlabel要素で記述します
<div data-role="fieldcontain">
<label for="name">名前</label>
<input type="text" id="name">
</div>


問い合わせフォーム

<!DOCTYPE html> 
<html lang="ja"> 
<head> 
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<title>jQuery Mobile Sample</title> 
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css">
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.js"></script>
<style>
.ui-content .h1{
color:#5E87B0;
font-size:17px;
text-shadow:1px 0 0 #FFF;
}
</style>
</head>
<body>
<div data-role="page" id="contact" data-theme="c">
<div data-role="header" data-theme="b">
<a href="#index" data-icon="arrow-l" data-direction="reverse">TOP</a>
<h1>スマートフォン</h1>
</div>
<div data-role="content">
<h2 class="h1">お問い合わせ</h2>
<form action="#" method="post">
<div data-role="fieldcontain"> 
<label for="name">名前</label>
<input type="text" id="name">
</div>
</form>
</div>
<div data-role="footer" data-theme="b">
<h4>&copy; 2012 スマートフォン</h4>
</div>
</div>
</body>
</html>


実行すると、jQuery Mobileで定義されているCSSによってフォーム部品が表示されます。


このフォームは、端末の向きに応じてjQuery Mobileがレイアウトを自動的に調整してくれます
縦向き(portrait)のときはラベルの下にフォーム部品が表示され、横向き(landscape)のときはラベルの右にフォーム部品が表示さます。



入力フォームの装飾と調整

  • ページの見出し(h2要素)とフォームの間には不自然な余白があり、入力フォームの背景色がやや暗いので、デザインを少し調整します
<div data-role="fieldcontain">
<label for="name">名前</label>
<input type="text" id="name">
</div>
/*入力フォームの背景を調整*/
textarea.ui-body-b,input.ui-body-b{
  background-color:white;
}
/*見出しとフォーム部品の間の余白を調整*/
.ui-field-contain:first-child{
  padding-top:0;
}

複数行入力ボックスの最適化

  • 「お問い合わせ内容」は複数行入力ボックスですので、textarea要素で記述します
<div data-role="fieldcontain">
<label for="comment">お問い合わせ内容</label>
<textarea id="comment"></textarea>
</div>
  • この複数行入力ボックスは、入力したテキストの長さに応じて自動的にフォームの高さが変わります


フリップスイッチを表示する

  • jQuery Mobile独自のフォーム部品「フリップスイッチ」を使って「性別」の項目を作ります
  • フリップスイッチは2つの選択肢からどちらか一方を選択するUIです
  • select要素にdata-role属性「slider」を指定し、内側に2つのoption要素で項目を指定すると表示できます
<div data-role="fieldcontain">
<label for="gender">性別</label>
<select name="gender" id="gender" data-role="slider" data-theme="b">
<option value="男性">男性</option>
<option value="女性">女性</option>
</select> 
</div>


使いやすいチェックボックスの追加

  • 「お問い合わせ種別」は、チェックボックスを使います
  • チェックボタンはスマートフォンで非常に使いにくいフォーム部品の1つですが、jQuery Mobileを利用すると指で選択しやすい形状に最適化できます
  • チェックボックスは、type属性に「checkbox」を指定したinput要素で記述し、label要素でラベルと関連付けます
  • チェックボックス全体はdata-role属性に「controlgroup」を指定したfieldset要素で包み、fieldset要素の先頭にlegend要素で項目名(ここでは「お問い合わせ種別」)を記述します
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>お問い合わせ種別</legend> 
<input type="checkbox" name="type1" id="type1" value="HTML5+CSS3"> 
<label for="type1">HTML5+CSS3</label> 
<input type="checkbox" name="type2" id="type2" value="WordPress+CMS">
<label for="type2">WordPress+CMS</label> 
<input type="checkbox" name="type3" id="type3" value="PHP+MySQL"> 
<label for="type3">PHP+MySQL</label> 
<input type="checkbox" name="type4" id="type4" value="SEO"> 
<label for="type4">SEO</label>
</fieldset>
</div>


送信・キャンセルボタンの装飾

  • フォームの送信・キャンセルボタンを作ってフォームを完成させます
  • jQuery Mobileではスマートフォンで操作しやすいボタンのUIが用意されており、以下のように記述するだけで表示できます
<form action="form.php" method="post">
<div data-role="fieldcontain"> 
<input type="submit" value="キャンセル" data-theme="b" data-inline="true">
<input type="submit" value="送信" data-theme="b" data-inline="true">
</form>


  • input要素にdata-icon属性を追加すると、それぞれのボタンにjQuery Mobileが用意したアイコンも表示できます
  • キャンセルボタンに×アイコンを、送信ボタンに右矢印のアイコンを付けます
<form action="form.php" method="post">
<div data-role="fieldcontain"> 
<input type="button" value="キャンセル" data-theme="b" data-icon="delete" data-inline="true">
<input type="submit" value="送信" data-theme="b" data-icon="arrow-r" data-inline="true">
</form>

<!DOCTYPE html>
<html lang="ja">
<head>
<title>お問い合わせ</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.css">
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.js"></script>
<style>
fieldcontain {
  margin: 0;
  padding: 0;
}
h2 {
  font-size: 1.2em;
  margin: 0;
  padding: 0;
}
</style>
</head>
<body>
<div data-role="page">
<div data-role="header">
<h1>お問い合わせフォーム</h1>
</div><!-- /header -->

<div data-role="content">
<h2>jQuery Mobileのフォーム</h2>
<form id="Contents" action="form.php" method="post">

<div data-role="fieldcontain">
<label for="name">名前:</label>
<input type="text" name="name" id="name" value="" placeholder="お名前を入力">
</div>

<div data-role="fieldcontain">
<label for="email">メールアドレス:</label>
<input type="email" id="email" name="url" placeholder="メールアドレス入力">
</div>

<div data-role="fieldcontain">
<label for="gender">性別:</label>
<select name="gender" id="gender" data-role="slider">
<option value="男性">男性</option>
<option value="女性">女性</option>
</select>
</div>

<div  data-role="fieldcontain">
<fieldset data-role="controlgroup">
<legend>該当するものを選んでください。</legend>
<input type="checkbox" name="checkbox[]" id="check1" value="iPhone" data-theme="d">
<label for="check1">iPhone</label>
<input type="checkbox" name="checkbox[]" id="check2" value="Android" data-theme="d">
<label for="check2">Android</label>
<input type="checkbox" name="checkbox[]" id="check3" value="Windows Phone" data-theme="d">
<label for="check3">Windows Phone</label>
</fieldset>
</div>

<div data-role="fieldcontain">
<label for="textarea-a">お問い合わせ内容:</label>
<textarea name="textarea" id="textarea"></textarea>
</div>

<div data-role="fieldcontain">
<label for="select-choice">オプション:</label>
<select name="shipper" id="shipper">
<option>選択してください</option>
<option value="プロフェッショナル">プロフェッショナル</option>
<option value="スタンダード">スタンダード</option>
<option value="エンタープライズ">エンタープライズ</option>
</select>
</div>

<div data-role="fieldcontain">
<label for="slider">満足度:</label>
<input type="range" name="slider" id="slider" value="0" min="0" max="5" data-theme="d" data-track-theme="d">
</div>

<div class="ui-grid-a">
<div class="ui-block-a">
<input type="reset" id="reset" data-theme="c" value="リセット">
</div>
<div class="ui-block-b">
<input type="submit" id="submit" data-theme="b" value="送信">
</div>
</div>

</form>
</div><!-- /content -->

<div data-role="footer" data-position="fixed">
<h4>フッター</h4>
</div><!-- /footer --> 
</div><!-- /page -->

</body>
</html>
  • 2つ以上の選択肢があり複数選択可能な場合は、配列で設定
  • 2つ以上の選択肢があっても1つしか選択できない場合は、配列で設定しなくても可能
  • 以下の例は、変数をその都度取得する記述を書いていますが、まとめて変数を取得する記述でも可能です
  • 「data-role="fieldcontain"」も同様に、ひとつの値ごとに閉じれば「区切り線」がつき、全体をまとめて閉じればつきません
<!DOCTYPE html> 
<html> 
<head> 
<title>タイトル</title> 
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.css">
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.js"></script>
<style>
h2 {
  font-size: 1.2em;
  margin: 0;
  padding: 0;
}
p {
  text-indent: 2em;
}
</style>
</head> 
<body> 

<div data-role="page" data-add-back-btn="true" data-back-btn-text="戻る">

<div data-role="header" data-position="inline">
<h1>確認画面</h1>
</div><!-- /header -->

<div data-role="content">

<div data-role="fieldcontain">
<h2>【お名前】</h2>
<p>
<?php echo htmlspecialchars($_POST['name'], ENT_QUOTES); ?>
</p>

<h2>【メールアドレス】</h2>
<p>
<?php echo htmlspecialchars($_POST['url'], ENT_QUOTES); ?>
</p>

<h2>【性別】</h2>
<p>
<?php echo htmlspecialchars($_POST['gender'], ENT_QUOTES); ?>
</p>

<h2>【機種選択】</h2>
<p><?php 
if ($_POST['checkbox'] != '') {
echo implode(", ", $_POST['checkbox']);
};
?>
</p>

<h2>【お問い合わせ内容】</h2>
<p>
<?php echo htmlspecialchars($_POST['textarea'], ENT_QUOTES); ?>
</p>

<h2>【オプション】</h2>
<p>
<?php echo ($_POST['shipper']);?>
</p>

<h2>【満足度】</h2>
<p>
<?php echo htmlspecialchars($_POST['slider'], ENT_QUOTES); ?></p>
</div> 
</div><!-- /content -->

<div data-role="footer" data-position="fixed">
<h4>フッター</h4>
</div><!-- /footer -->
</div><!-- /page -->

</body>
</html>

Maps JavaScript API

http://maps.google.com/maps/api/staticmap?center=<緯度>,<軽度>&zoom=<ズーム値>&size=<画像横幅>x<画像縦幅>&sensor=<ユーザーの位置情報取得センサーの使用の有無>
ルート検索マップ
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Google Maps API SDK</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<!-- 【1】Google Maps APIを呼び出し-->
<script src="http://maps.google.com/maps/api/js?sensor=true&language=ja"></script>

<!-- 【2】どんな地図を描くかのメイン処理 -->
<script type="text/javascript">

// ■地図初期化し表示
function initialize(position) {

  // ■地図を表示する緯度経度を指定する
  var latlng = new google.maps.LatLng(35.681382,139.766084);
  
  // ■地図必須プロパティを設定
  var myOptions = {
    
    // ■ズームレベルの指定 0〜17
    zoom: 15,
    
    // ■地図の中心を指定(上記で設定の緯度経度latlng)
    center: latlng,
    
    // ■地図のタイプ設定
    //  ROADMAP:デフォルト、SATELLITE:写真タイル、HYBRID:写真タイルと主要な機能、TERRAIN:物理的な起伏を示すタイル
    mapTypeId: google.maps.MapTypeId.ROADMAP
    
  };// 地図プロパティここまで
  
  //ルート検索
  var rendererOptions = 
  {
  draggable: true,
  preserveViewport:false
  };
  var directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
  var directionsService = new google.maps.DirectionsService();
    var request =
    {
    origin: "東京",//出発点
    destination: "京都",//到着点
    travelMode: google.maps.DirectionsTravelMode.DRIVING,//運転モード
    unitSystem: google.maps.DirectionsUnitSystem.METRIC,
    optimizeWaypoints: true,
    avoidHighways: false,
    avoidTolls: false
    };
    directionsService.route(request, function(response, status)
    {
        if (status == google.maps.DirectionsStatus.OK)
        {
        directionsDisplay.setDirections(response);
        }
    });
  
  // ■<div id="map_canvas">と結びつけて、その領域に地図を描く
  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  // ルート検索地図に表示する
  directionsDisplay.setMap(map);
  
}//initialize() 

</script>

</head>
<body onload="initialize()">

<!-- 地図はここに描画される -->
<div id="map_canvas" style="width: 640px; height: 480px;"></div>

</body>
</html>


ルート検索マップ(詳細)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Google Maps API SDK</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<!-- 【1】Google Maps APIを呼び出し-->
<script src="http://maps.google.com/maps/api/js?sensor=true&language=ja"></script>

<!-- 【2】どんな地図を描くかのメイン処理 -->
<script type="text/javascript">

// ■地図初期化し表示
function initialize(position) {

  // ■地図を表示する緯度経度を指定する
  var latlng = new google.maps.LatLng(35.681382,139.766084);
  
  // ■地図必須プロパティを設定
  var myOptions = {
    
    // ■ズームレベルの指定 0〜17
    zoom: 15,
    
    // ■地図の中心を指定(上記で設定の緯度経度latlng)
    center: latlng,
    
    // ■地図のタイプ設定
    //  ROADMAP:デフォルト、SATELLITE:写真タイル、HYBRID:写真タイルと主要な機能、TERRAIN:物理的な起伏を示すタイル
    mapTypeId: google.maps.MapTypeId.ROADMAP
    
  };// 地図プロパティここまで
  
  //ルート検索
  var rendererOptions = 
  {
  draggable: true,
  preserveViewport:false
  };
  var directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
  var directionsService = new google.maps.DirectionsService();
    var request =
    {
    origin: "東京",//出発点
    destination: "京都",//到着点
    travelMode: google.maps.DirectionsTravelMode.DRIVING,//運転モード
    unitSystem: google.maps.DirectionsUnitSystem.METRIC,
    optimizeWaypoints: true,
    avoidHighways: false,
    avoidTolls: false
    };
    directionsService.route(request, function(response, status)
    {
        if (status == google.maps.DirectionsStatus.OK)
        {
		directionsDisplay.setDirections(response);
		directionsDisplay.setPanel(document.getElementById("directionsPanel"));
        }
    });
  
  // ■<div id="map_canvas">と結びつけて、その領域に地図を描く
  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  // ルート検索地図に表示する
  directionsDisplay.setMap(map);
  
}//initialize() 

</script>

</head>
<body onload="initialize()">

<!-- 地図はここに描画される -->
<div id="map_canvas" style="width: 640px; height: 480px;"></div>
<div id="directionsPanel" style="width: 640px; height: 480px;"></div>

</body>
</html>




池袋駅からここまでのルートマップ
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<!--iPhone用フルスクリーン表示設定-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<title>ルートマップ</title>
<style>
  html, body {
    margin:0;
    padding:0;
  }
  div#map_canvas {
    width:330px;
    height:370px;
  }	
</style>
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script>
  var map;
  var directionsDisplay = new google.maps.DirectionsRenderer;
  var directionsService = new google.maps.DirectionsService();
  function initialize() {
    var myOptions = {
      center:new google.maps.LatLng(35.7242235, 139.7153773),
      zoom: 13,
      mapTypeId:google.maps.MapTypeId.ROADMAP,
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
    directionsDisplay.setMap(map); 
    calcRoute();
  }
   
  var end = new google.maps.LatLng(35.724442,139.715447);
  function calcRoute() {
      var request = {
      origin: "池袋駅",
      destination:end,
      travelMode: google.maps.DirectionsTravelMode.WALKING,
      optimizeWaypoints: true,
      };
      directionsService.route(request, function(response, status) {
          if (status == google.maps.DirectionsStatus.OK) {
            directionsDisplay.setDirections(response);
          }
      });
  }
</script>
</head>
<body onload="initialize();">
<div id="map_canvas" style="width: 320px; height: 480px;"></div>
</body>
</html>


ルート検索

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Google Maps ルート検索</title>
<style>
#map_canvas {
    width:700px;
    height:500px;
}
#container {
  width: 700px;
  margin: auto;
}
</style>
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="mapdrow.js"></script>
</head>
<body onload="initialize();">
<div id="container">
<h1>Google Maps ルート検索</h1>
<p>テキストフィールドに始点と終点を入力すると、ルート検索を行います。</p>
<p>
始点:<input type="text" name="start" id="start" size="12" maxlength="100">
 &nbsp;終点:<input type="text" name="end" id="end" size="12" maxlength="100">
</p>
<p>
 <input type="button" value="検索" onclick="searchRoute()" />
</p>
<div id="map_canvas"></div>
</div>
</body>
</html>


《mapdrow.js》

var map;
var directionsRenderer;
var directions;
var err = google.maps.DirectionsStatus;
var directionsErr = new Array();
directionsErr[err.INVALID_REQUEST] = "指定された DirectionsRequest が無効です。";
directionsErr[err.MAX_WAYPOINTS_EXCEEDED] = "DirectionsRequest に指定された DirectionsWaypoint が多すぎます。ウェイポイントの最大許容数は 8 に出発地点と到着地点を加えた数です。";
directionsErr[err.NOT_FOUND] = "出発地点、到着地点、ウェイポイントのうち、少なくとも 1 つがジオコード化できませんでした。";
directionsErr[err.OVER_QUERY_LIMIT] = "ウェブページは、短期間にリクエストの制限回数を超えました。";
directionsErr[err.REQUEST_DENIED] = "ウェブページではルート サービスを使用できません。";
directionsErr[err.UNKNOWN_ERROR] = "サーバー エラーのため、ルート リクエストを処理できませんでした。もう一度試すと正常に処理される可能性があります。";
directionsErr[err.ZERO_RESULTS] = "出発地点と到着地点間でルートを見つけられませんでした。";

  //onload時の処理
  function initialize() {

  //Google Maps初期設定
    var opts = {
	zoom:14,
	center:new google.maps.LatLng(35.681382,139.766084),
	mapTypeId:google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas") , opts);		    //Google Maps作成
	
  //ルートレンダラ生成
  directionsRenderer = new google.maps.DirectionsRenderer({
    polylineOptions: {
    strokeColor: '#FF0000',
    strokeWeight: 4,
    strokeOpacity: 0.7
    }
  });	
  directionsRenderer.setMap(map);	//ルートレンダラにマップを関連付け
  }

  //検索開始
  function searchRoute() {

  //textboxからの値を取得
  var start = document.getElementById("start").value;
  var end = document.getElementById("end").value;

  //検索設定
  directions = new google.maps.DirectionsService();	//ルート生成

  //ルートリクエスト
  directions.route({
    origin:start,		//開始地点
    destination:end,	//終了地点
    travelMode:google.maps.DirectionsTravelMode.DRIVING,	//ルートタイプ(車)
    avoidHighways:true,		//高速道路(使わない)
    avoidTolls:true,		//有料道路(使わない)
    optimizeWaypoints: true,	//最適化された最短距離にする。
  },
  function(results, status) {	//ルート結果コールバック関数
    if (status == err.OK) {	//検索結果がtrueの場合
	directionsRenderer.setDirections(results);
    } else {	//検索結果がfalseの場合
	alert(directionsErr[status]);
    }
    });
}