問い合わせフォームの作成
フォームUIの課題とjQuery Mobileによる解決
- スマートフォンのブラウザーに標準で用意されているフォーム部品はどれもサイズが小さく、そのままでは指による操作に適しません
- スマートフォンサイトの制作ではフォームをスマートフォンに最適化することが重要です
フォーム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>© 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>
送信・キャンセルボタンの装飾
<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"> 終点:<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]); } }); }