案例描述

如图所示,通过点击加号或者减号按钮会自动计算小计的价格。逻辑非常简单。

在线展示:https://antmoe.gitee.io/project/2020/04/18/shop.html

实现过程

逻辑非常简单,获取值计算即可。

  1. 为两个按钮分别绑定事件
  2. 改变值
  3. 返回

代码实现

加号与减号逻辑几乎一样,以加号示例。

首先我们得HTML结构为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<table id="cart">
<tr>
<th><input type="checkbox" name="" id="checkall"> 全选</th>
<th>商品</th>
<th>单价</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<tr>
<td><input type="checkbox" name="check"></td>
<td>【49元1件88元2件118元3件】富贵鸟春季新品长袖格子男 509灰色 XL</td>
<td><span name="price">2599.00</span></td>
<td>
<button name="minus" disabled>-</button>
<input type="text" name="count" value="1">
<button name="add">+</button>
</td>
<td><span name="subtotal">2599.00</span></td>
<td><a href="#" name="delete">删除</a></td>
</tr>
<tr>
<td><input type="checkbox" name="check"></td>
<td>【49元1件88元2件118元3件】富贵鸟春季新品长袖格子男 509灰色 XL</td>
<td><span name="price">2599.00</span></td>
<td>
<button name="minus" disabled>-</button>
<input type="text" name="count" value="1">
<button name="add">+</button>
</td>
<td><span name="subtotal">2599.00</span></td>
<td><a href="#" name="delete">删除</a></td>
</tr>
<tr>
<td><input type="checkbox" name="check"></td>
<td>【49元1件88元2件118元3件】富贵鸟春季新品长袖格子男 509灰色 XL</td>
<td><span name="price">2599.00</span></td>
<td>
<button name="minus" disabled>-</button>
<input type="text" name="count" value="1">
<button name="add">+</button>
</td>
<td><span name="subtotal">2599.00</span></td>
<td><a href="#" name="delete">删除</a></td>
</tr>
</table>
  1. 应该找到每一个加号按钮,并分别为其绑定事件。并且我们需要知道用户到底点击的是哪一个按钮。

    1
    2
    3
    4
    5
    6
    7
    8
    // 获取所有加号按钮
    var addButtons = document.getElementsByName('add')
    for (var i = 0; i < addButtons.length; i++) {
    // 因为获取的按钮是一个集合,因此需要取出每一项来添加事件
    var addButton = addButtons[i]
    // 为每个按钮添加点击事件,传入参数event
    addButton.onclick = function (event) {}
    }
  2. 接下来要做的就是获取输入框,并改变其值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 获取当前点击的按钮
    var targetButton = event.target
    // 操作与之关联的input元素 (存在空白节点问题,所以需要连续向上取两次)
    var inputElement = targetButton.previousSibling.previousSibling
    // 获取数量(string类型需要转化为Number类型)
    var count = Number(inputElement.value)
    // 计算
    count += 1
    // 将值更新到页面
    inputElement.value = count
    // 获取减按钮(这里需要判断减按钮是否可以被点击)
    var minuButton = inputElement.previousSibling.previousSibling
    if (count > 1) {
    minuButton.removeAttribute('disabled')
    } else {
    minuButton.setAttribute('disabled', 'disabled')
    }
  3. 既然可以改变输入框内容了,那接下来就是要计算小计的价格了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 通过当前点击的按钮获取父级节点
    var btnTdElement = targetButton.parentNode
    // 通过按钮的父级节点获取单价节点
    var priTdElement = btnTdElement.previousSibling.previousSibling
    // 获取单价
    var priceElement = priTdElement.childNodes[1]
    // 将单价转换为浮点型
    var price = parseFloat(priceElement.firstChild.nodeValue)
    // 计算总值
    var subtotal = price *= count
    // 判断是否有小数点,没有添加
    if (String(subtotal).indexOf('.') === -1) {
    subtotal = subtotal + '.00'
    }
    // 通过按钮的父级节点获取下一个兄弟元素即总价节点
    var subTdElement = btnTdElement.nextSibling.nextSibling
    // 获取总价
    var subtotalElement = subTdElement.childNodes[1]
    // 改变总价的文本
    subtotalElement.textContent = subtotal
  4. 主要功能都完成了,接下来就是细节了,即最前边的复选框和背景颜色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 通过按钮的父级节点获取父级节点
    var trElement = btnTdElement.parentNode
    // 通过上一步找到的tr元素节点 获取第一个子节点即复选框节点
    var checkTdElement = trElement.childNodes[1]
    // 获取复选框节点的input节点
    var checkboxElement = checkTdElement.firstChild
    // 设置值
    checkboxElement.setAttribute('checked', 'checked')
    // 将当前这一行的背景颜色修改 #fff4e8
    trElement.style.backgroundColor = '#fff4e8'
  5. 至于减按钮功能类似

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    for (var i = 0; i < minusButtons.length; i++) {
    var minusButton = minusButtons[i]
    minusButton.onclick = function (event) {
    var targetButton = event.target
    // 操作与之关联的input元素
    var inputElement = targetButton.nextSibling.nextSibling
    console.log(inputElement)
    // 获取数量(string类型)
    var count = Number(inputElement.value)
    // 计算
    count -= 1
    // 将值更新到页面
    inputElement.value = count
    // 计算小计
    var btnTdElement = targetButton.parentNode
    var priTdElement = btnTdElement.previousSibling.previousSibling
    var priceElement = priTdElement.childNodes[1]
    var price = parseFloat(priceElement.firstChild.nodeValue)
    var subtotal = price *= count
    if (String(subtotal).indexOf('.') === -1) {
    subtotal = subtotal + '.00'
    }
    var subTdElement = btnTdElement.nextSibling.nextSibling
    var subtotalElement = subTdElement.childNodes[1]
    subtotalElement.textContent = subtotal
    // console.log(price)

    // 操作减按钮
    if (count <= 1) {
    targetButton.setAttribute('disabled', 'disabled')
    // 复选框
    var trElement = btnTdElement.parentNode
    var checkTdElement = trElement.childNodes[1]
    var checkboxElement = checkTdElement.firstChild
    checkboxElement.removeAttribute('checked')
    // 将当前这一行的背景颜色修改 #fff4e8
    trElement.style.backgroundColor = 'white'
    }

    }
    }
  6. 删除按钮

    完成了加减后,删除按钮就很简单了。但是需要注意几点:

    ①删除的是当前所在按钮的父级,也就是tr标签

    ②删除后需要将上方的总数修改-1

    ③就是a标签的跳转

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 删除按钮
    var delButtons = document.getElementsByName("delete")
    for (var i = 0; i < delButtons.length; i++) {
    var delButton = delButtons[i]
    delButton.onclick = function (event) {
    var targetButton = event.target
    var currentTrElement = targetButton.parentNode.parentNode
    var parentElement = currentTrElement.parentNode
    parentElement.removeChild(currentTrElement)
    //修改上方总数量
    var numberElement = document.getElementById('number')
    var number = parseInt(numberElement.textContent)
    number -= 1
    numberElement.textContent = number
    /*
    阻止a标签跳转
    1. 通过调用调用 preventDefault()
    2. 时间的处理函数 return 语句 -> return false
    */
    event.preventDefault()
    }
    }
  7. 全选

    tip:判断复选框的checked属性即可知道是否被选择。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    var checkall = document.getElementById("checkall")
    checkall.onclick = function () {
    console.log(1)
    console.log(checkall.checked)
    var checkElements = document.getElementsByName("check")
    var cart = document.getElementById('cart')
    var trElements = cart.getElementsByTagName('tr')
    if (checkall.checked) {
    for (var i = 0; i < checkElements.length; i++) {
    var checkElement = checkElements[i]
    checkElement.setAttribute("checked", "checked")
    }
    for (var i = 1; i < trElements.length; i++) {
    var trElement = trElements[i]
    trElement.style.backgroundColor = '#fff4e8'
    }

    } else {
    for (var i = 0; i < checkElements.length; i++) {
    var checkElement = checkElements[i]
    checkElement.removeAttribute('checked')
    }
    for (var i = 1; i < trElements.length; i++) {
    var trElement = trElements[i]
    trElement.style.backgroundColor = 'transparent'
    }
    }

    }

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车案例</title>
<style>
* {
box-sizing: border-box;
}

.cart-container {
margin: 100px auto;
width: 990px;
}

#title {
width: 100px;
color: #c81623;
padding: 10px 0px;
margin: 0;
border-bottom: 1px solid #c81623;
}

#cart {
width: 990px;
border: 1px solid #d9d9d9;
border-collapse: collapse;
}

#cart th,
#cart td {
height: 80px;
border-bottom: 1px solid #d9d9d9;
text-align: center;

}

#cart th {
background: #d9d9d9;
}

#cart td:nth-of-type(1) {
width: 60px;
text-align: center;
}

#cart td:nth-of-type(2) {
width: 400px;
}

#cart td:nth-of-type(3) {
width: 80px;
text-align: right;
}

#cart td:nth-of-type(4) {
width: 250px;
padding: 0 80px;
}

#cart td:nth-of-type(5) {
width: 80px;
text-align: right;
}

#cart td:nth-of-type(6) {
/* width: 80px; */
text-align: center;
}

#cart td:nth-of-type(4) button,
#cart td:nth-of-type(4) input {
display: block;
float: left;
height: 25px;
border: 1px solid #d9d9d9;
}

#cart td:nth-of-type(4) button {
width: 20px;
background-color: #fff;
}

#cart td:nth-of-type(4) input {
padding: 0;
border-width: 1px;
border-left: none;
border-right: none;
width: 50px;
outline: none;
text-align: center;
}

#cart td:nth-of-type(4) input:focus {
background-color: #f1f1f1;
}

#cart td a {
text-decoration: none;
color: #333333;
}

#cart td a:hover {
color: #c81623;
}
</style>
</head>

<body>
<div class="cart-container">
<!-- 购物车标题+商品数量 -->
<h3 id="title">全部商品<span id="number">3</span></h3>
<!-- 购物车表格 -->
<table id="cart">
<tr>
<th><input type="checkbox" name="" id="checkall"> 全选</th>
<th>商品</th>
<th>单价</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<tr>
<td><input type="checkbox" name="check"></td>
<td>【49元1件88元2件118元3件】富贵鸟春季新品长袖格子男 509灰色 XL</td>
<td><span name="price">2599.00</span></td>
<td>
<button name="minus" disabled>-</button>
<input type="text" name="count" value="1">
<button name="add">+</button>
</td>
<td><span name="subtotal">2599.00</span></td>
<td><a href="#" name="delete">删除</a></td>
</tr>
<tr>
<td><input type="checkbox" name="check"></td>
<td>【49元1件88元2件118元3件】富贵鸟春季新品长袖格子男 509灰色 XL</td>
<td><span name="price">2599.00</span></td>
<td>
<button name="minus" disabled>-</button>
<input type="text" name="count" value="1">
<button name="add">+</button>
</td>
<td><span name="subtotal">2599.00</span></td>
<td><a href="#" name="delete">删除</a></td>
</tr>
<tr>
<td><input type="checkbox" name="check"></td>
<td>【49元1件88元2件118元3件】富贵鸟春季新品长袖格子男 509灰色 XL</td>
<td><span name="price">2599.00</span></td>
<td>
<button name="minus" disabled>-</button>
<input type="text" name="count" value="1">
<button name="add">+</button>
</td>
<td><span name="subtotal">2599.00</span></td>
<td><a href="#" name="delete">删除</a></td>
</tr>
</table>
</div>
<!-- 结算 -->
<script>
// 获取到购物车中所有加按钮
var addButtons = document.getElementsByName('add')
var minusButtons = document.getElementsByName('minus')
console.log(minusButtons)
// 事件的绑定 -> 每次只能绑定一个元素

for (var i = 0; i < addButtons.length; i++) {
var addButton = addButtons[i]
// 绑定鼠标事件
// 修改数量
addButton.onclick = function (event) {
console.log('click')
/*
通过event对象获取用户点击的是哪个按钮
* 绑定事件的元素 -> 所有的<botton name='add'>+</botton>
* 触发事件的元素 -> 只能有一个
*/
// console.log(event.currentTarget, event.target, this)
var targetButton = event.target
// 操作与之关联的input元素
var inputElement = targetButton.previousSibling.previousSibling
// console.log(inputElement)
// 获取数量(string类型)
var count = Number(inputElement.value)
// 计算
count += 1
// 将值更新到页面
inputElement.value = count
// 操作与之关联的减按钮
var minuButton = inputElement.previousSibling.previousSibling
if (count > 1) {
minuButton.removeAttribute('disabled')
} else {
minuButton.setAttribute('disabled', 'disabled')
}
// 更新与之关联的小计
// 获取单价
var btnTdElement = targetButton.parentNode
var priTdElement = btnTdElement.previousSibling.previousSibling

var priceElement = priTdElement.childNodes[1]
var price = parseFloat(priceElement.firstChild.nodeValue)
var subtotal = price *= count
if (String(subtotal).indexOf('.') === -1) {
subtotal = subtotal + '.00'
}
var subTdElement = btnTdElement.nextSibling.nextSibling
var subtotalElement = subTdElement.childNodes[1]
subtotalElement.textContent = subtotal
// console.log(price)

// 复选框
var trElement = btnTdElement.parentNode
var checkTdElement = trElement.childNodes[1]
var checkboxElement = checkTdElement.firstChild
checkboxElement.setAttribute('checked', 'checked')

// 将当前这一行的背景颜色修改 #fff4e8
trElement.style.backgroundColor = '#fff4e8'
}
}
for (var i = 0; i < minusButtons.length; i++) {
var minusButton = minusButtons[i]
minusButton.onclick = function (event) {
var targetButton = event.target
// 操作与之关联的input元素
var inputElement = targetButton.nextSibling.nextSibling
console.log(inputElement)
// 获取数量(string类型)
var count = Number(inputElement.value)
// 计算
count -= 1
// 将值更新到页面
inputElement.value = count


// 计算小计
var btnTdElement = targetButton.parentNode
var priTdElement = btnTdElement.previousSibling.previousSibling

var priceElement = priTdElement.childNodes[1]
var price = parseFloat(priceElement.firstChild.nodeValue)
var subtotal = price *= count
if (String(subtotal).indexOf('.') === -1) {
subtotal = subtotal + '.00'
}
var subTdElement = btnTdElement.nextSibling.nextSibling
var subtotalElement = subTdElement.childNodes[1]
subtotalElement.textContent = subtotal
// console.log(price)

// 操作减按钮
if (count <= 1) {
targetButton.setAttribute('disabled', 'disabled')
// 复选框
var trElement = btnTdElement.parentNode
var checkTdElement = trElement.childNodes[1]
var checkboxElement = checkTdElement.firstChild
checkboxElement.removeAttribute('checked')
// 将当前这一行的背景颜色修改 #fff4e8
trElement.style.backgroundColor = 'white'
}

}
}
// 删除按钮
var delButtons = document.getElementsByName("delete")
for (var i = 0; i < delButtons.length; i++) {
var delButton = delButtons[i]
delButton.onclick = function (event) {
var targetButton = event.target

var currentTrElement = targetButton.parentNode.parentNode
var parentElement = currentTrElement.parentNode
parentElement.removeChild(currentTrElement)

var numberElement = document.getElementById('number')
var number = parseInt(numberElement.textContent)
number -= 1
numberElement.textContent = number
/*
阻止a标签跳转
1. 通过调用调用 preventDefault()
2. 时间的处理函数 return 语句 -> return false
*/
event.preventDefault()
}
}

// 全选
var checkall = document.getElementById("checkall")
checkall.onclick = function () {
console.log(1)
console.log(checkall.checked)
var checkElements = document.getElementsByName("check")
var cart = document.getElementById('cart')
var trElements = cart.getElementsByTagName('tr')
if (checkall.checked) {
for (var i = 0; i < checkElements.length; i++) {
var checkElement = checkElements[i]
checkElement.setAttribute("checked", "checked")
}
for (var i = 1; i < trElements.length; i++) {
var trElement = trElements[i]
trElement.style.backgroundColor = '#fff4e8'
}

} else {
for (var i = 0; i < checkElements.length; i++) {
var checkElement = checkElements[i]
checkElement.removeAttribute('checked')
}
for (var i = 1; i < trElements.length; i++) {
var trElement = trElements[i]
trElement.style.backgroundColor = 'transparent'
}
}

}

</script>
</body>

</html>