在***的HTML 5标准中,为***的各类浏览器带来了拖拽功能。这意味着现在可以不通过其他框架如jQuery的辅助就能在页面上拖拽各种元素。在本文中,将指导读者认识 HTML 5中的拖拽功能,并且使用它来打造一款基本的购物车。这个购物车很简单,我们只往其中放置一件商品然后检查是否已有商品了,如果已经存在同样的商品,则更 新其数量和价格。本文要求读者有初步的HTML 5基础知识和一定基础的Javascript知识即可。
开始
首先我们需要设计购物车的基础结构和准备一系列的商品。为了简单演示使用了各类商品,这里只是使用HTML 5中的data_*属性(可参考https://developer.mozilla.org/en-US/docs/Web/Guide/HTML /Using_data_attributes),为各个商品添加了价格。简单来说,data_属性是让用户可以为某些标签存储一些额外的数据信息。代码如下所示:
- <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 。该事件在当文档的状态发生改变的时候触发,我们期望将其状态设置为“complete”,直到附加上剩余的事件和逻辑代码。这个跟jQuery中的.ready事件类似。代码如下:
- addEvent(document, 'readystatechange', function() {
- if ( document.readyState !== "complete" )
- return true;
- });
对DOM查询
在HTML 5标准中,DOM的选择器方面也进行了更新,其中新增的功能中最有用的方法就是querySelectorAll。这个方法允许用户使用更复杂的css样式选择器(有点象jQuery)去查询页面元素。这比旧有的通过class和ID去进行查询简单些。
下面是其中一个例子:
- var items = document.querySelectorAll("section.products ul li");
- var cart = document.querySelectorAll("#cart ul")[0];
这里,首先是分别使用items和cart变量获得商品列表和购物车列表,留作稍候使用。可以看到,上面通过querySelectorAll的方 法比以往更加方便了。既然已经有了产品的列表,那么我们将实现拖拽功能并且当产品被拖拽的时候,必须要有相关的事件进行监听。为了实现这个目的,将循环遍 历商品列表并且使用setAttribute方法设置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。这个方法的目的是设置拖拽的选项并且保存元素的id留作稍候通过使用 dataTransfer.setData方法获得数据。dataTransfer事件对象允许我们指定默认的拖拽动作的效果。默认是既复制并移动,但这 里我们强制令其为只移动元素。代码如下:
- 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#
设计购物车
我们先来看下设计的购物车大概是什么样子的,如下图:
看上去样子不大漂亮,但这个并不影响我们的示例教学用。可以看到用户可以拖拉在Product List中的商品到上面的购物车区域中。
默认元素是不接收drop事件的,因此为了能让某个元素能接收到拖拽的事件消息,我们要重写默认的行为。为了实现这个目的,使用了onDragOver方法。代码如下,所做的其实是阻止默认的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的值就可以从商品列表中找到商品。但要注意的是在往购物车中放商品前要检查该商品是否已经放置在里面了,
检查的方法很简单,只需要使用querySelectorAll方法就可以了,代码如下:
- var exists = document.querySelectorAll("#cart ul li[data-id='" + id + "']");
接下来就很容易根据变量exists去判断是否购物车中已经存在商品,代码如下:
- if(exists.length > 0){
- updateCartItem(exists[0]);
- } else {
- addCartItem(item, id);
- }
在上面的代码中,如果购物车中不包含任何商品,则调用下面的代码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方法中要做的事是克隆当前的商品项。现在可以指定的data-*的值。首先在克隆后的结点中,设置的是data-id结点,然后设置data-quantity属性为1并移除id属性(id属性在页面中是唯一的)。然后我们增加两个新的span到列表项中,这是用来显示小计项和产品的数量的。下图是从商品列表中拖拉到购物车中的情景:
更新购物车中的商品
如果一个商品已经在购物车中存在了,我们将要增加其数量,其中我们显示给用户的方式是单价*数量,因此,我们使用 getAttribute方法就可以获得当前的数量并且对其进行增加的操作,代码如下:
- 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#
更新总价格
一旦购物车的商品数量增加了,我们就要重新计算总价格。这里我们再次使用了
querySelectorAll功能。我们只需要遍历购物车中的每一个商品并重新计算价格就可以了,这里是取了两位小数位。
- 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);
从下图中可以看到当拖拽多个商品到购物车中,商品的总价格是会增加的:
***我们总体看下所有的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中获得下载。