一步步教你如何用HTML 5拖拽功能打造購物車
譯文在***的HTML 5標(biāo)準(zhǔn)中,為***的各類瀏覽器帶來了拖拽功能。這意味著現(xiàn)在可以不通過其他框架如jQuery的輔助就能在頁面上拖拽各種元素。在本文中,將指導(dǎo)讀者認(rèn)識 HTML 5中的拖拽功能,并且使用它來打造一款基本的購物車。這個購物車很簡單,我們只往其中放置一件商品然后檢查是否已有商品了,如果已經(jīng)存在同樣的商品,則更 新其數(shù)量和價格。本文要求讀者有初步的HTML 5基礎(chǔ)知識和一定基礎(chǔ)的Javascript知識即可。
開始
首先我們需要設(shè)計購物車的基礎(chǔ)結(jié)構(gòu)和準(zhǔn)備一系列的商品。為了簡單演示使用了各類商品,這里只是使用HTML 5中的data_*屬性(可參考https://developer.mozilla.org/en-US/docs/Web/Guide/HTML /Using_data_attributes),為各個商品添加了價格。簡單來說,data_屬性是讓用戶可以為某些標(biāo)簽存儲一些額外的數(shù)據(jù)信息。代碼如下所示:
- <section id="cart" class="shopping-cart">
- <ul>
- </ul>
- <span class="total">0.00</span>
- </section>
- <section id="products" class="products">
- <ul>
- <li id="product-1" data-price="2.00"><span>Product 1</span></li>
- <li id="product-2" data-price="3.00"><span>Product 2</span></li>
- <li id="product-3" data-price="2.99"><span>Product 3</span></li>
- <li id="product-4" data-price="3.50"><span>Product 4</span></li>
- <li id="product-5" data-price="4.25"><span>Product 5</span></li>
- <li id="product-6" data-price="6.75"><span>Product 6</span></li>
- <li id="product-7" data-price="1.99"><span>Product 7</span></li>
- </ul>
- </section>
由于本文是主要講解使用Javascript搭配HTML 5的新功能,因此不會使用大家常用的jQuery。下面編寫addEvent事件,代碼如下:
- function addEvent(element, event, delegate ) {
- if (typeof (window.event) != 'undefined' && element.attachEvent)
- element.attachEvent('on' + event, delegate);
- else
- element.addEventListener(event, delegate, false);
- }
我們將使用的***個事件是readystatechange 。該事件在當(dāng)文檔的狀態(tài)發(fā)生改變的時候觸發(fā),我們期望將其狀態(tài)設(shè)置為“complete”,直到附加上剩余的事件和邏輯代碼。這個跟jQuery中的.ready事件類似。代碼如下:
- addEvent(document, 'readystatechange', function() {
- if ( document.readyState !== "complete" )
- return true;
- });
對DOM查詢
在HTML 5標(biāo)準(zhǔn)中,DOM的選擇器方面也進(jìn)行了更新,其中新增的功能中最有用的方法就是querySelectorAll。這個方法允許用戶使用更復(fù)雜的css樣式選擇器(有點(diǎn)象jQuery)去查詢頁面元素。這比舊有的通過class和ID去進(jìn)行查詢簡單些。
下面是其中一個例子:
- var items = document.querySelectorAll("section.products ul li");
- var cart = document.querySelectorAll("#cart ul")[0];
這里,首先是分別使用items和cart變量獲得商品列表和購物車列表,留作稍候使用。可以看到,上面通過querySelectorAll的方 法比以往更加方便了。既然已經(jīng)有了產(chǎn)品的列表,那么我們將實(shí)現(xiàn)拖拽功能并且當(dāng)產(chǎn)品被拖拽的時候,必須要有相關(guān)的事件進(jìn)行監(jiān)聽。為了實(shí)現(xiàn)這個目的,將循環(huán)遍 歷商品列表并且使用setAttribute方法設(shè)置draggable屬性為true,這里并且要為每個商品都添加dragstart事件。代碼如下:
- for (var i = 0; i < items.length; i++) {
- var item = items[i];
- item.setAttribute("draggable", "true");
- addEvent(item, 'dragstart', onDrag);
- };
在上面的代碼中,請注意將dragstart方法委托定義為onDrag。這個方法的目的是設(shè)置拖拽的選項(xiàng)并且保存元素的id留作稍候通過使用 dataTransfer.setData方法獲得數(shù)據(jù)。dataTransfer事件對象允許我們指定默認(rèn)的拖拽動作的效果。默認(rèn)是既復(fù)制并移動,但這 里我們強(qiáng)制令其為只移動元素。代碼如下:
- function onDrag(event){
- event.dataTransfer.effectAllowed = "move";
- event.dataTransfer.dropEffect = "move";
- var target = event.target || event.srcElement;
- var success = event.dataTransfer.setData('Text', target.id);
- }
#p#
設(shè)計購物車
我們先來看下設(shè)計的購物車大概是什么樣子的,如下圖:
看上去樣子不大漂亮,但這個并不影響我們的示例教學(xué)用??梢钥吹接脩艨梢酝侠赑roduct List中的商品到上面的購物車區(qū)域中。
默認(rèn)元素是不接收drop事件的,因此為了能讓某個元素能接收到拖拽的事件消息,我們要重寫默認(rèn)的行為。為了實(shí)現(xiàn)這個目的,使用了onDragOver方法。代碼如下,所做的其實(shí)是阻止默認(rèn)的dragover和dragenter事件行為:
- function onDragOver(event){
- if(event.preventDefault) event.preventDefault();
- if (event.stopPropagation) event.stopPropagation();
- else event.cancelBubble = true;
- return false;
- }
- addEvent(cart, 'dragover', onDragOver);
接下來,我們要往購物車中增加商品了。只需要使用dataTransfer.getData方法從dataTransfer對象中取出id的值,有了id的值就可以從商品列表中找到商品。但要注意的是在往購物車中放商品前要檢查該商品是否已經(jīng)放置在里面了,
檢查的方法很簡單,只需要使用querySelectorAll方法就可以了,代碼如下:
- var exists = document.querySelectorAll("#cart ul li[data-id='" + id + "']");
接下來就很容易根據(jù)變量exists去判斷是否購物車中已經(jīng)存在商品,代碼如下:
- if(exists.length > 0){
- updateCartItem(exists[0]);
- } else {
- addCartItem(item, id);
- }
在上面的代碼中,如果購物車中不包含任何商品,則調(diào)用下面的代碼addCartItem。
- function addCartItem(item, id) {
- var clone = item.cloneNode(true);
- clone.setAttribute('data-id', id);
- clone.setAttribute('data-quantity', 1);
- clone.removeAttribute('id');
- var fragment = document.createElement('span');
- fragment.setAttribute('class', 'quantity');
- fragment.innerHTML = ' x 1';
- clone.appendChild(fragment);
- fragment = document.createElement('span');
- fragment.setAttribute('class', 'sub-total');
- clone.appendChild(fragment);
- cart.appendChild(clone);
- }
如果購物車中不包含某件商品,則addCartItem方法中要做的事是克隆當(dāng)前的商品項(xiàng)?,F(xiàn)在可以指定的data-*的值。首先在克隆后的結(jié)點(diǎn)中,設(shè)置的是data-id結(jié)點(diǎn),然后設(shè)置data-quantity屬性為1并移除id屬性(id屬性在頁面中是唯一的)。然后我們增加兩個新的span到列表項(xiàng)中,這是用來顯示小計項(xiàng)和產(chǎn)品的數(shù)量的。下圖是從商品列表中拖拉到購物車中的情景:
更新購物車中的商品
如果一個商品已經(jīng)在購物車中存在了,我們將要增加其數(shù)量,其中我們顯示給用戶的方式是單價*數(shù)量,因此,我們使用 getAttribute方法就可以獲得當(dāng)前的數(shù)量并且對其進(jìn)行增加的操作,代碼如下:
- function updateCartItem(item){
- var quantity = item.getAttribute('data-quantity');
- quantity = parseInt(quantity) + 1
- item.setAttribute('data-quantity', quantity);
- var span = item.querySelectorAll('span.quantity');
- span[0].innerHTML = ' x ' + quantity;
- }
#p#
更新總價格
一旦購物車的商品數(shù)量增加了,我們就要重新計算總價格。這里我們再次使用了
querySelectorAll功能。我們只需要遍歷購物車中的每一個商品并重新計算價格就可以了,這里是取了兩位小數(shù)位。
- function updateCart(){
- var total = 0.0;
- var cart_items = document.querySelectorAll("#cart ul li")
- for (var i = 0; i < cart_items.length; i++) {
- var cart_item = cart_items[i];
- var quantity = cart_item.getAttribute('data-quantity');
- var price = cart_item.getAttribute('data-price');
- var sub_total = parseFloat(quantity * parseFloat(price));
- cart_item.querySelectorAll("span.sub-total")[0].innerHTML = " = " + sub_total.toFixed(2);
- total += sub_total;
- }
- document.querySelectorAll("#cart span.total")[0].innerHTML = total.toFixed(2);
從下圖中可以看到當(dāng)拖拽多個商品到購物車中,商品的總價格是會增加的:
***我們總體看下所有的Javascript代碼如下所示。
- function addEvent(element, event, delegate ) {
- if (typeof (window.event) != 'undefined')
- element.attachEvent('on' + event, delegate);
- else
- element.addEventListener(event, delegate, false);
- }
- addEvent(document, 'readystatechange', function() {
- if ( document.readyState !== "complete" )
- return true;
- var items = document.querySelectorAll("section.products ul li");
- var cart = document.querySelectorAll("#cart ul")[0];
- function updateCart(){
- var total = 0.0;
- var cart_items = document.querySelectorAll("#cart ul li")
- for (var i = 0; i < cart_items.length; i++) {
- var cart_item = cart_items[i];
- var quantity = cart_item.getAttribute('data-quantity');
- var price = cart_item.getAttribute('data-price');
- var sub_total = parseFloat(quantity * parseFloat(price));
- cart_item.querySelectorAll("span.sub-total")[0].innerHTML = " = " + sub_total.toFixed(2);
- total += sub_total;
- }
- document.querySelectorAll("#cart span.total")[0].innerHTML = total.toFixed(2);
- }
- function addCartItem(item, id) {
- var clone = item.cloneNode(true);
- clone.setAttribute('data-id', id);
- clone.setAttribute('data-quantity', 1);
- clone.removeAttribute('id');
- var fragment = document.createElement('span');
- fragment.setAttribute('class', 'quantity');
- fragment.innerHTML = ' x 1';
- clone.appendChild(fragment);
- fragment = document.createElement('span');
- fragment.setAttribute('class', 'sub-total');
- clone.appendChild(fragment);
- cart.appendChild(clone);
- }
- function updateCartItem(item){
- var quantity = item.getAttribute('data-quantity');
- quantity = parseInt(quantity) + 1
- item.setAttribute('data-quantity', quantity);
- var span = item.querySelectorAll('span.quantity');
- span[0].innerHTML = ' x ' + quantity;
- }
- function onDrop(event){
- if(event.preventDefault) event.preventDefault();
- if (event.stopPropagation) event.stopPropagation();
- else event.cancelBubble = true;
- var id = event.dataTransfer.getData("Text");
- var item = document.getElementById(id);
- var exists = document.querySelectorAll("#cart ul li[data-id='" + id + "']");
- if(exists.length > 0){
- updateCartItem(exists[0]);
- } else {
- addCartItem(item, id);
- }
- updateCart();
- return false;
- }
- function onDragOver(event){
- if(event.preventDefault) event.preventDefault();
- if (event.stopPropagation) event.stopPropagation();
- else event.cancelBubble = true;
- return false;
- }
- addEvent(cart, 'drop', onDrop);
- addEvent(cart, 'dragover', onDragOver);
- function onDrag(event){
- event.dataTransfer.effectAllowed = "move";
- event.dataTransfer.dropEffect = "move";
- var target = event.target || event.srcElement;
- var success = event.dataTransfer.setData('Text', target.id);
- }
- for (var i = 0; i < items.length; i++) {
- var item = items[i];
- item.setAttribute("draggable", "true");
- addEvent(item, 'dragstart', onDrag);
- };
- });
本文的demo可以在http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/09/cart.html中看到,
完整代碼可以在http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/09/cart.zip中獲得下載。