一、功能实现
(1)在页面左侧区域,单击“可选座位”,将座位设置为“已选座位”,一次最多选5
个座位。右侧座位号汇总区域显示已选中的座位号,并显示电影票总价。
(2)在页面左侧区域,单击“已选座位”,将座位设置为“可选座位”。右侧选座信息汇总区域取消相应的座位号,并显示电影票总价。
(3)单击页面右侧选座信息汇总区域座位号右上角的“×”,将座位设置为“可选座位”。选座信息汇总区域取消相应的座位号,并显示电影票总价。
(4)“已选座位”数量不为0时,“确认选座”按钮设置为可用状态,选座信息汇总区域显示。“已选座位”数量为0时,“确认选座”按钮设置为不可用状态,选座信息汇总区域隐藏。
案例在Chrome浏览器运行效果,如图9-1所示。
二、HTML布局
1.页面左侧选座区域布局
由图9-1可知,左侧选座区域是8排10列,以第7排为例,介绍左侧选座区域布局,示例代码如下。CSS代码详见本书源码。
<div class="seats-wrapper">
<div class="row">
<span class="seat selectable" data-column-id="9" data-row-id="7" </span>
<span class="seat selected" data-column-id="8" data-row-id="7" </span>
<span class="seat selected" data-column-id="7" data-row-id="7" </span>
<span class="seat selectable" data-column-id="6" data-row-id="7" </span>
<span class="seat selectable" data-column-id="5" data-row-id="7" </span>
<span class="seat selectable" data-column-id="4" data-row-id="7" </span>
<span class="seat selectable" data-column-id="3" data-row-id="7" </span>
<span class="seat empty" data-column-id="" data-row-id="7" data-st="E" </span>
<span class="seat sold" data-column-id="2" data-row-id="7" data-st="LK" </span>
<span class="seat sold" data-column-id="1" data-row-id="7" data-st="LK" </span>
</div>
</div>
上述代码中,“<div class="seats-wrapper">”是选座区域的容器,1个“<div class="row">”元素代表一排,1个span元素代表1个座位。座位的class属性值中,“seat”代表公共样式、“selectable”代表可选座位、“selected”代表已选座位、“empty”代表不显示座位、“sold”代表座位已经售出;座位的“data-column-id”属性代表座位号;“data-row-id”属性代表座位在第几排;“data-st”属性代表座位状态,若是值等于“E”代表座位不显示,若是值等于“LK”代表座位被锁定。
2.页面右侧选座信息汇总区域和票价区域布局
由图9-1可知,右侧选座信息汇总区域已选两个座位,分别是“7排8座”和“7排7座”,总价60,“确认选座”按钮处于可用状态。HTML示例代码如下,CSS代码详见本书源码。
1 <div class="ticket-info">
2 <div class="no-ticket" style="display:none">
3 <p class="buy-limit">座位:一次最多选5个座位</p>
4 <p class="no-selected">请<span>点击左侧</span>座位图选择座位</p>
5 </div>
6 <div class="has-ticket" style="display:block">
7 <span class="text">座位:</span>
8 <div class="ticket-container" data-limit="5">
9 <span data-row-id="7" data-column-id="8" data-index="7-8" class="ticket">7排8座</span>
10 <span data-row-id="7" data-column-id="7" data-index="7-7" class="ticket">7排7座</span>
11 </div>
12 <div class="total-price">
13 <span>总价 :</span>
14 <span class="price">60</span>
15 </div>
16 </div>
上述代码中,第1行代码“<div class="ticket-info">”是本区域的容器;第2行代码“<div class="no-ticket" style="display:none">”是没有选中座位的容器,“已选座位”数量不为0时,它的状态是隐藏;第6行代码“<div class=" has-ticket" style="display:block">”是有选中座位的容器,“已选座位”数量不为0时,它的状态是显示;第8行代码“<div class="ticket-container" data-limit="5">”是座位号信息的容器;第9-10行代码中的1个span元素代表一个座位号信息,它的class属性值是“ticket”;第12行代码“<div class="total-price">”是电影票总价的容器;第14行代码“<span class="price">60</span>”显示电影票总价。
三、工具函数
由功能描述(1)、(2)、(3)可知,选中或取消选中座位时都需要重新计算电影票总价,因此将计算电影票总价功能封装成函数showTotalbill(),以便在选中或取消选中座位时调用。示例代码如下。
var total_bill = 0 ;//总价
var pricePerTicked = 30;//单价
function showTotalbill(){
total_bill = document.querySelectorAll(".ticket").length * pricePerTicked;
document.querySelector('.price').innerHTML = total_bill;
}
上述代码声明了函数showTotalbill(),它的功能是计算电影票总价并显示在相应的位置。其中,变量total_bill代表电影票总价;变量pricePerTicked代表电影票单价;电影票总价的值是电影票单价乘以电影票数量。
由功能描述(4)可知,“已选座位”数量不为0时,“确认选座”按钮设置为可用状态,选座信息汇总区域显示。“已选座位”数量为0时,“确认选座”按钮设置为不可用状态,选座信息汇总区域隐藏。每次选中或取消座位时,都将执行上述过程,因此将此功能封装成函数setTicketsinfoState ()。示例代码如下。
1 function setTicketsinfoState (){
2 if(document.querySelectorAll(".ticket").length>0){
3 document.querySelector('.confirm-btn').classList.remove('disable');
4 document.querySelector('.no-ticket').style.display = 'none';
5 document.querySelector('.has-ticket').style.display = 'block';
6 }
7 else{
8 document.querySelector('.confirm-btn').classList.add('disable');
9 document.querySelector('.no-ticket').style.display = 'block';
10 document.querySelector('.has-ticket').style.display = 'none';
11 }
12 }
上述代码中,第1行代码声明了函数setTicketsinfoState();第2-11行代码判断选中座位的集合长度,如果大于0,将“确认选座”按钮设置为可用状态,选座信息汇总区域显示;否则,执行相反的操作。
四、选座
在页面左侧区域,单击“可选座位”,将座位设置为“已选座位”,单击“已选座位”,将座位设置为“可选座位”,一次最多选5个座位,同时右侧座位汇总区域显示或取消已选中的座位号,并显示电影票总价。选座程序流程图如图9-2所示。
由于左侧选座区域是8排10列,如果为每一个座位绑定单击事件,比较消耗资源,因此可以通过事件委托为座位的父元素“<div class="seats-wrapper">”绑定单击事件。示例代码如下。
1 document.querySelector('.seats-wrapper').onclick = function (e) {
2 if (e.target.nodeName!="SPAN"||e.target.getAttribute('data-st') == 'E' || e.target.getAttribute('data-st') == 'LK') {
3 return;
4 }
5 else if (e.target.classList.contains('selected')) {
6 e.target.classList.remove('selected');
7 e.target.classList.add('selectable');
8 var tickets = document.querySelectorAll(".ticket");
9 var selectd = e.target.getAttribute('data-row-id') + '-' + e.target.getAttribute('data-column-id');
10 for (var i = 0; i < tickets.length; i++) {
11 if (tickets[i].getAttribute('data-index') == selectd) {
12 document.querySelector(".ticket-container").removeChild(tickets[i]);
13 break;
14 }
15 }
16 showTotalbill();
17 } else if (document.querySelectorAll(".ticket").length >= 5) {
18 alert('一次最多买5张');
19 }
20 else{
21 e.target.classList.remove('selectable');
22 e.target.classList.add('selected');
23 var newUL = document.querySelector(".ticket-container");
24 var s = document.createElement('span');
25 s.setAttribute('data-row-id', e.target.getAttribute('data-row-id'));
26 s.setAttribute('data-column-id', e.target.getAttribute('data-column-id'));
27 s.setAttribute('data-index',e.target.getAttribute('data-row-id')+'-'+e.target.getAttribute('data-column-id'));
28 s.className = 'ticket';
29 s.innerText = e.target.getAttribute('data-row-id') + '排' + e.target.getAttribute('data-column-id') + '座';
30 newUL.appendChild(s);
31 showTotalbill();
32 }
33 setTicketsinfoState();
34 }
上述代码中,第1行代码利用事件委托,为所有座位的父元素绑定单击事件及其处理程序;第2行代码判断如果单击的不是座位,或者单击的座位已锁定或empty,则程序返回;第5-16行代码判断如果单击的座位已经处于选中状态,则取消它的选中状态,删除右侧对应座位号,并计算和显示电影票总价;第17-19行代码判断如果单击的座位处于未选中状态且已选中座位大于5个,则弹出提示框提示;第21-32行代码实现将选中的座位设置为选中状态,在右侧选座信息汇总区域添加座位信息,并计算和显示电影票总价;第33行代码调用封装的函数setTicketsinfoState()设置右侧选座信息汇总区域状态。
五、取消选座
单击页面右侧选座信息汇总区域座位号右上角的“×”,将座位设置为“可选座位”,同时选座信息汇总区域取消相应的座位号,并显示电影票总价。由于座位号可能有5个,如果为每1个座位号绑定单击事件,比较消耗资源,因此可以通过事件委托给座位号的父元素“<div class="ticket-container"></div>”绑定单击事件及其处理程序。示例代码如下。
1 document.querySelector('.ticket-container').onclick = function (e) {
2 if (document.querySelectorAll(".ticket").length <= 0) {
3 return;
4 }
5 var selected = document.querySelectorAll(".selected");
6 var ticket = e.target.getAttribute('data-index');
7 for (var i = 0; i < selected.length; i++) {
8 if (selected[i].getAttribute('data-row-id') + '-' + selected[i].getAttribute('data-column-id') == ticket)
9 {
10 selected[i].classList.remove('selected');
11 selected[i].classList.add('selectable');
12 break;
13 }
14 }
15 e.target.parentNode.removeChild(e.target);
16 showTotalbill();
17 setTicketsinfoState();
18 }
上述代码中,第1行代码利用事件委托,为所有座位号的父元素绑定单击事件及其处理程序;第2行代码判断如果座位号的集合长度小于0,则程序返回;第5-15行代码将删除的座位号对应的座位设置成可选的状态;第16-17行代码设置右侧选座信息汇总区域显示或隐藏,并计算和显示电影票总价。