[JS] 疑似買い物アプリ改

タグ :

これまでの基礎知識を結集して簡易アプリを作成するシリーズです。
今回は過去に作成した買い物アプリを改変します。

過去に作成した買い物アプリは商品のチェックボックスをクリックすることでカゴリストに商品を追加するというものです。今回はそのアプリにドラッグ&ドロップ機能を搭載します。

アプリ概要

仕様は過去に作成したものから大きく3つほど変更点があります。

以下のような仕様とします。

ドラッグ&ドロップで買い物
 - 各商品をドラッグすることができる
 - ドラッグした商品をカゴ上にドロップすることでカゴリストへ追加する
カゴリストに個数を追加
 - 商品のドロップ回数に応じて個数も加算する
 - 一回のドロップに対して加算される個数は 1 とする
 - 合計値も値段 × 個数で算出して求める
カゴリスト内の順番はドロップされた順
 - ドロップされた商品順に上からリストへ追加されていく

必要となる処理は以下になります。

  • ドラッグ&ドロップ機能の設置と設定
  • ドロップ後の表示変更処理
  • ドロップした要素の情報取得
  • リスト内データの取得
  • リスト部の要素変更(追加・削除)
  • 合計値の算出

作成したサンプル

処理の流れとコードは以下になります。

[ 動作フロー ]
js_shop2_flow
[ コード ]
/* shop2.html */
$(function() {
	$('.item1,.item2,.item3,.item4,.item5').draggable({
		containment: '.items',
		revert: true,
		helper: 'clone',
		drag: function() {
			$(this).addClass('dragout');
		},
		stop: function() {
			$(this).removeClass('dragout');
		}
	});
	var flg = true;
	$('.kago').droppable({
		accept: '.item1,.item2,.item3,.item4,.item5',
		tolerance: 'touch',
		over: function(e, ui) {
			var obj = ui.draggable.attr('class').split(" ");
			var tag = '.' + obj[0];
			$(tag).addClass('dragout');
		},
		out: function(e, ui) {
			$('.ui-draggable-dragging').removeClass('dragout');
		},
		deactivate: function(e, ui) {
			ui.draggable.draggable({ revert: flg });
			flg = true;
		},
		drop: function(e, ui) {
			ui.draggable.removeClass('dragout');
			listUp(ui, $(this));
			flg = false;
		}
	});
	function listUp(ui, obj) {
		var out;
		var val = 0, calc = 0, num = -1;
		var tData = new Array;
		var dData = new Array;
		var tObj = $('.list table tbody').html();
		dData = ui.draggable.attr('value').split(" ");
		$('td',tObj).each(function(i) {
			tData[i] = $(this).text();
			if(tData[i] == dData[0]) {
				num = i + 1;
			}
		});
		if(tData.length) {
			calc = parseInt(tData[tData.length - 2]);
			val = parseInt(tData[tData.length - 1]);
			$('.list table tbody tr:last').remove();
		}
		if(num >= 0) {
			var hPrice = parseInt(dData[1]) + parseInt(tData[num]);
			var hVal = parseInt(tData[num + 1]) + 1;
			$('.list table tbody td:eq(' + num + ')').text(hPrice + "円");
			$('.list table tbody td:eq(' + (num + 1) + ')').text(hVal);
		} else {
			out = "<tr><td>" + dData[0] + "</td>";
			out += "<td>" + dData[1] + "円</td>";
			out += "<td>1</td></td></tr>";
			$(".list table tbody").append(out);
		}
		calc += parseInt(dData[1]);
		val++;
		out = "<tr><td>合計</td>";
		out += "<td>" + calc + "円</td>";
		out += "<td>" + val + "</td></tr>";
		$(".list table tbody").append(out);
	}
});
<h4>疑似買い物アプリ</h4>
<div class="items">
	<p class="head">欲しい商品を右下のカゴに入れてね。</p>
	<div>
		<p>パソコン</p>
		<div class="item1" value="パソコン 100000"></div>
		<p>100,000円</p>
	</div>
	<div>
		<p>HD</p>
		<div class="item2" value="HD 30000"></div>
		<p>30,000円</p>
	</div>
	<div>
		<p>メモリ</p>
		<div class="item3" value="メモリ 20000"></div>
		<p>20,000円</p>
	</div>
	<div>
		<p>キーボード</p>
		<div class="item4" value="キーボード 3000"></div>
		<p>3,000円</p>
	</div>
	<div>
		<p>マウス</p>
		<div class="item5" value="マウス 1000"></div>
		<p>1,000円</p>
	</div>
	<div>
		<div class="kago"><p>カゴ</p></div>
	</div>
</div>
<div class="list">
	<p>[買い物カゴの中身]</p>
	<table>
	<tr>
		<th>商品名</th>
		<th>値段</th>
		<th>個数</th>
	</tr>
	</table>
</div>
body {
	margin-left: 10px; }
.items {
	margin-top: 10px;
	width: 310px; height: 350px; background: #ccc; }
.items p.head {
	padding-top: 10px; margin-left: 20px; }
.items > div {
	float: left; margin-top: 10px; margin-left: 10px;
	width: 90px; height: 140px; text-align: center;  }
.items > div p {
	margin-top: 0px; margin-bottom: 0px; }
.item1, .item2, .item3, .item4, .item5 {
	width: 90px; height: 90px;
	background: blue; cursor: move; }
.kago {
	margin-top: 30px; margin-left: 5px;
	width: 80px; height: 80px; background: gray; }
.kago > p {
	padding-top: 30px; }
.list {
	margin-top: -350px; margin-left: 330px;
	width: 250px; }
.list table, .list th, .list td {
	border: 1px #888 solid; }
.list table th, .list table td {
	width: 100px; height: 30px; }
.list table tr:first-child, .list table tr:last-child {
	background: #eee; }
.list table td {
	text-align: center; }
.list table td:nth-child(2) {
	text-align: right; }
.list table tr:last-child {
	font-weight: bold; }
.dragout {
	opacity: 0.4; }
詳しい説明は後述します。
各商品をドラッグすることができます。その商品をカゴ部分にドラッグするとカゴリストにその商品名と値段が追加されます。同じ商品をドロップすることで個数が加算されていきます。

コード解説

jQuery のコードの説明をします。

[ jQuery の説明 ]
[ 3行目〜13行目 商品のドラッグ設定 ]
$('.item1,.item2,.item3,.item4,.item5').draggable({

商品をドラッグできるように設定します。セレクターにドラッグできる HTML 要素のクラス名を指定します。今回は全ての商品をドラッグ可能となるように指定します。

containment: '.main',
revert: true,
helper: 'clone',

ドラッグ可動範囲の指定(containment)、ドラッグ後に元の位置に戻る動作の指定(revert)、ドラッグ時に要素の複製表示する指定(helper)を行います。

drag: function() {
	$(this).addClass('dragout');
},

ドラッグ開始直後に、その要素に ‘dragout’ というクラス名を追加します。このクラス名を追加すると、CSS にてその要素の透明度が「0.4」となり、薄く表示されます。つまり、ドラッグ中は元の位置にある要素がちょっと薄く表示されるようにしています。

stop: function() {
	$(this).removeClass('dragout');
}

ドラッグ終了時には、開始時に追加したクラス名 ‘dragout’ を取り除く処理をします。この処理により、元のボックス要素が薄く表示されるのはドラッグ中のみとなります。

[ 14行目〜35行目 カゴへのドロップ設定 ]
var flg = true;

ドラッグ後に元の位置に戻る動作をするか否かを可変するためのフラグ変数を宣言します。このフラグはドロップ領域内全体で使用したいので、doroppable() メソッドの外に出しました。この変数は draggable() のオプション「revert」に直接設定する値を代入します。初期値は true で元の位置に戻る設定をするようにしています。

$('.kago').droppable({

買い物カゴへのドロップの設定をします。商品一覧部分の右下にある「カゴ」と記述された領域に商品をドロップすることが出来るようにします。セレクターにドロップ領域の HTML 要素のクラス名を指定します。

accept: '.item1,.item2,.item3,.item4,.item5',
tolerance: 'touch',

カゴへドロップ可能とする要素は、全ての商品なので全ての商品のクラス名を指定します。
ドロップ可能位置は ‘touch’ とし、領域に少しでも被った時点でドロップ可能となります。

over: function(e, ui) {
	var obj = ui.draggable.attr('class').split(" ");
	var tag = '.' + obj[0];
	$(tag).addClass('dragout');
},

ドロップ領域上にドラッグ要素がある状態の時の動作を指定します。ドロップ領域上にある、つまりドロップ可能状態である場合には、現在ドラッグしている要素のクラス名に「dragout」というクラス名が追加されるようにしています。このクラス名はドラッグ開始時に元の要素に追加するクラス名と同じですが、それを流用して、over 時にも追加させます。そうすることで over 時にちょっと色が薄くなるようになります。

out: function(e, ui) {
	$('.ui-draggable-dragging').removeClass('dragout');
},

over 時の動作の逆で、ドロップ領域からドラッグ要素が外れた場合に動作します。処理も over とは逆で、「dragout」というクラス名を削除します。削除すると色が元の透明度に戻ります。

deactivate: function(e, ui) {
	ui.draggable.draggable({ revert: flg });
	flg = true;
},

ドロップ終了時に要素が元に戻る動作をさせるか否かを設定し直します。設定値には、変数 flg を使用しています。そのため、ドロップ領域内にドロップされたときは、元の位置に戻らないように設定され、領域外にドロップされたときは元の位置に戻るように設定されます。

drop: function(e, ui) {
	ui.draggable.removeClass('dragout');
	listUp(ui, $(this));
	flg = false;
}

ドロップ時に動作する処理を記述します。ユーザー定義した関数「listUp()」を呼び出して、HTML 表示変更処理を行います。関数の引数には、ドラッグ要素のオブジェクト変数とドロップ要素のオブジェクト変数を渡します。

[ 41行目〜48行目 リストデータの取得&商品比較 ]
var tObj = $('.list table tbody').html();

リスト内の要素を変数「tObj」に格納します。この変数は後にリスト内データを取得するときに使用します。

dData = ui.draggable.attr('value').split(" ");

ドラッグ要素の value 値を変数「dData」に格納します。value 値には商品名と値段を「スペース」区切りで登録してあるので、それぞれを文字列分割関数で区切って配列に格納します。dData[0] には商品名、dData[1] には値段が格納されます。

$('td',tObj).each(function(i) {

リストに表示してあるデータの検索を行います。リストに何行表示されているかが分からないので、each() を用いてループ処理にて表示中の全てのデータから検索します。

tData[i] = $(this).text();
if(tData[i] == dData[0]) {
	num = i + 1;
}

ループ中のリストの中からドロップした商品が存在するかを調べます。リスト内データを変数配列「tData」に順番に格納しつつ、ドロップした商品と同じであった場合にその要素番号を変数「num」に格納しておきます。この変数は後の値段と個数の変更処理にて使用します。

[ 49行目〜53行目 合計値の取得&合計行の削除 ]
if(tData.length) {

リスト内にデータがあるかを判定します。変数配列「tData」の要素数が 0 ならリストには何も表示されていないと判定します。データがあるなら、合計値に関する処理を行います。データが無いなら、初期状態ということになるので何もしません。

calc = parseInt(tData[tData.length - 2]);

合計の値段を取得します。合計行はリストの最後のデータになるので、変数配列「tData」の要素数分から -1 した要素には合計値段が格納されています。「-2」となっているのは、配列の開始要素が 0 から始まるため、-1 だけ余分に減算しています。

val = parseInt(tData[tData.length - 1]);

合計の個数を取得します。合計行はリストの最後のデータになるので、変数配列「tData」の最終要素には合計個数が格納されています。配列の開始要素が 0 から始まるため、-1 だけ余分に減算しています。

$('.list table tbody tr:last').remove();

合計行を削除します。セレクターにてリスト部の最終行を指定します。

[ 54行目〜58行目 値段と個数の変更 ]
if(num >= 0) {

ドロップした商品がリストに既に表示されているかどうかを判定します。商品比較時で使用した変数「num」には、その商品情報が格納された変数配列の要素番号が格納されています。初期値には -1 を代入してあるので、0 以上、つまりリストに表示中の商品をドロップした場合には次の処理を行います。

var hPrice = parseInt(dData[1]) + parseInt(tData[num]);

リストに表示する用の商品の値段を変数「hPrice」に格納します。値段は「現在表示中の値段」+「商品の値段」になります。

var hVal = parseInt(tData[num + 1]) + 1;

リストに表示する用の商品の個数を変数「hVal」に格納します。個数は「現在表示中の値段」+「1」になります。

$('.list table tbody td:eq(' + num + ')').text(hPrice + "円");

リストに表示中の商品の値段を変更します。セレクターの指定には、eq() によりリストの商品番号を指定するために変数「num」を使用します。

$('.list table tbody td:eq(' + (num + 1) + ')').text(hVal);

リストに表示中の商品の個数を変更します。セレクターの指定には、eq() によりリストの商品番号を指定するために変数「num」を使用します。

[ 60行目〜63行目 リストに商品を追加 ]
out = "<tr><td>" + dData[0] + "</td>";
out += "<td>" + dData[1] + "円</td>";
out += "<td>1</td></td></tr>";

リストに追加するデータを HTML 要素と共に変数「out」に格納します。

$(".list table tbody").append(out);

リストの最終行にデータを追加します。

[ 65行目〜70行目 合計行の追加 ]
calc += parseInt(dData[1]);
val++;
out = "<tr><td>合計</td>";
out += "<td>" + calc + "円</td>";
out += "<td>" + val + "</td></tr>";

リストに追加する合計データを HTML 要素と共に変数「out」に格納します。合計行には合計値段と合計個数を表示します。合計値段は変数「calc」、合計個数は変数「val」を用いて計算しています。

$(".list table tbody").append(out);

リストの最終行に合計を追加します。

まとめ

前の買い物アプリではチェックボックスだけで、何か味気ないものでしたが、今回のドラッグ&ドロップ機能を使用したことによって、より買い物っぽくなりました。楽しんで操作できるということから、買い物の促進にも繋がるのではと思います。たぶん。。。

ただ、複数個カゴに入れたい場合等、一個ずつしか入れられないのが難点です。その他にもカゴから削除できないなど問題点は多数ありますが、今後改善版を作成していきたいと思います。

Share

  • このエントリーをはてなブックマークに追加

Comment

コメントを残す

*がついている欄は必須項目です。

  • Twitter
  • Facebook
  • Google Plus
  • RSS Feed