引言
本文将结合 HTML、CSS 和 JavaScript 的代码,详细介绍如何构建一个具备商品添加、编辑、删除以及数量增减功能的表格,同时实现操作提示框的动态显示。
整体功能
- 从 JSON 文件中获取商品数据并渲染到表格中。
- 支持对商品的数量进行增减操作,实时更新总价和总数量。
- 可以添加新的商品,同时对输入进行合法性验证。
- 能够编辑和删除已有的商品信息。
- 操作成功或失败时,会弹出相应的提示框。
效果展示
HTML 结构
<!-- 提示框的底板
提示的内容是不固定的,调用的时候往在里传-->
<div class="alertCenter">
<!-- 操作成功提示框,初始为隐藏状态 -->
<div id="bigAlert" style="display:none;">
<div class="alertContent">
<img src="./img/对勾.png" alt="" />
<span id="alertMessage"></span>
</div>
</div>
<!-- 操作成功提示框,初始为隐藏状态 -->
<div id="bigAlertErrorName" style="display: none;">
<div class=" alertContentError">
<img src="./img/叉号.png" alt="" />
<span 6+6+id="alertMessage"></span>
</div>
</div>
</div>
<!-- 页面上的表格 -->
<table id="productTable">
<!-- 表格的头部区域 -->
<thead>
<tr>
<th>商品名称</th>
<th class="price">价格</th>
<th>数量</th>
<th>操作</th>
</tr>
</thead>
<!-- 表格的内容区域 ,后续通过数据渲染动态添加-->
<tbody></tbody>
<!-- 表格的底部内容 -->
<tfoot>
<tr>
<td>总价:</td>
<td id="totalPrice">0</td>
<td>总数量:</td>
<td id="totalNumber">0</td>
</tr>
</tfoot>
</table>
<!-- 添加按钮 -->
<button class="addBtn" onclick="editAddBtn(0,1)">添加</button>
<!-- 添加商品和编辑商品的区域是一样的 ,但是需要重新写一个新的数据,
为了避免点击的时候触发的不是想要的效果(点的时候和不知道是添加商品还是编辑商品) -->
<!-- 添加新商品的输入框区域
开始的时候是隐藏的-->
<div class="hiddenText" style="display: none;">
<input type="text" id="productName" placeholder="商品名称">
<input type="text" id="productPrice" placeholder="商品价格">
<input type="text" id="productNum" placeholder="商品数量">
<button id="saveBtnChange" onclick="saveBtn(2)">保存</button>
<button id="cancelBtn"
onclick="document.getElementsByClassName('hiddenText')[0].style.display = 'none';">取消</button>
</div>
<!-- 编辑商品的输入框区域
开始的时候是隐藏的-->
<div class="hiddenTextChange" style="display: none;">
<input type="text" id="productNameChange" placeholder="商品名称">
<input type="text" id="productPriceChange" placeholder="商品价格">
<input type="text" id="productNumChange" placeholder="商品数量">
<button id="saveBtnChange" onclick="saveBtn(1)">保存</button>
<button id="cancelBtnChange"
onclick="document.getElementsByClassName('hiddenTextChange')[0].style.display = 'none';">取消</button>
</div>
JS部分
1.变量声明
data
:用于存储从 ./js/shopCarAbout.json
文件中获取并解析后的 JSON 数据。
maxId
:记录商品数据中最大的 id
值,后续添加新商品时会基于此生成新的 id
。
productId
:用于存储当前操作(编辑或删除)的商品的 id
。
// 存放解析后的 JSON 格式数据
let data;
//商品数据中最大的 id 值
let maxId;
// 商品的对应id(每一行的),后续通过商品的id能准确的删除对应的商品
let productId;
2.数据请求
XMLHttpRequest
是一个用于在浏览器和服务器之间进行异步通信的对象。
xhr.open('get', './js/shopCarAbout.json', true)
:使用 GET
请求方式从指定路径获取 JSON 数据,true
表示异步请求。
xhr.send()
:发送请求。
xhr.onreadystatechange
:当请求的状态发生变化时触发该回调函数。
xhr.readyState == 4 && xhr.status == 200
:readyState
为 4 表示请求已完成,status
为 200 表示请求成功。
data = JSON.parse(text)
:将响应的文本数据解析为 JSON 对象并存储在 data
变量中。
maxId = data[data.length - 1].id
:获取当前数据中最大的 id
值。
render(data)
:调用 render
函数将解析后的数据渲染到页面上。
let xhr = new XMLHttpRequest();
xhr.open('get', './js/shopCarAbout.json', true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
let text = xhr.responseText;
data = JSON.parse(text);
maxId = data[data.length - 1].id;
console.log(maxId);
render(data); // 数据加载成功后渲染页面
console.log(data);
}
};
3.数据渲染函数
str
:用于拼接要插入到表格中的 HTML 字符串。
totalPrice
:用于计算商品的总价。
totalNumber
:用于计算商品的总数量。
通过 for...in
循环遍历 data
数组,计算每个商品的总价和总数量,并将其累加到 totalPrice
和 totalNumber
中。
拼接每一行商品的 HTML 代码,包含商品名称、价格、数量增减按钮、编辑和删除按钮,并为按钮绑定相应的点击事件。document.getElementById('productTable').getElementsByTagName('tbody')[0].innerHTML = str
:将拼接好的 HTML 字符串插入到表格的 tbody
中。
upData(totalPrice, totalNumber)
:调用 upData
函数更新页面上显示的总价和总数量。
// 数据渲染函数,将商品数据渲染到表格中,并更新总价和总数量显示
function render(data) {
// 创一个空字符串,用于后面拼接字符串(渲染到页面上的)
let str = "";
// 商品的总价(后续会实时变化的)
let totalPrice = 0;
// 商品的数量总和(后续会实时变化的)
let totalNumber = 0;
// 通过循环 data 计算出总价和总数量
for (let i in data) {
// 总价=总数×价格
totalPrice += data[i].number * data[i].price;
// 总数=data 的下标(*1 是为了避免,总数在页面上是拼接的的形式出现)
totalNumber += data[i].number * 1;
str += `<tr>
<td>${data[i].name}</td>
<td>${data[i].price}</td>
<td>
<button class="jian" onclick="actionBtn(${i},2)">-</button>
<span class="num">${data[i].number}</span>
<button class="jia" onclick="actionBtn(${i},1)">+</button>
</td>
<td>
<button class="changeBtn" onclick="editAddBtn(${i},2)">编辑</button>
<button class="deleteBtn" onclick="actionBtn(${i},3)">删除</button>
</td>
</tr>`;
}
// 将拼接好的 str 赋值给 productTable 表格 tbody 元素的 innerHTML 属性,实现将商品数据行渲染到表格中
document.getElementById('productTable').getElementsByTagName('tbody')[0].innerHTML = str;
// 传参计算好的总价和总数量,更新页面上的内容
upData(totalPrice, totalNumber);
}
4.更新数据函数
函数接受 totalPrice
和 totalNumber
作为参数,通过 document.getElementById
方法获取页面上显示总价和总数量的元素,并使用 textContent
属性更新内容。
// 更新总价和总数量的函数
// 接受传来的参数
function upData(totalPrice, totalNumber) {
// 获取页面上 totalPrice 和 totalNumber 的元素(显示总价和总数量的单元格)
// 使用 textContent 属性将传入写进去,实现更新页面上显示的总价和总数量的功能。
document.getElementById('totalPrice').textContent = totalPrice;
document.getElementById('totalNumber').textContent = totalNumber;
}
5.加减删除按钮函数
index
:表示要操作的商品在 data
数组中的下标。
type
:表示操作类型,1 表示增加数量,2 表示减少数量,3 表示删除商品。
当 type
为 1 时,将对应商品的 number
属性加 1。当 type
为 2 时,若对应商品的 number
大于 0,则将其减 1。
当 type
为 3 时,使用 splice
方法从 data
数组中删除对应商品,并调用 showAlert
函数显示删除成功的提示框。
无论执行哪种操作,最后都调用 render
函数重新渲染页面,更新表格内容和总价、总数量。
// 加减删除按钮的函数 (合并过 ok)
// (index 是自己取的名字。这里的 index 是形参,接受参数的,
// 上面拼接字符串里面的 这个函数的点击事件传参了一个下标 i(这个 i 是是会更新的点哪个 下标就是对应的))
function actionBtn(index, type) {
// 通过下标将对应的数据增加 1
if (type == 1) {
console.log(data);
// data 现在是一个数组包对象(json 的数据)的形式出现的,index 是上面传来的下标 i(点哪个就是哪条商品的下标).点是访问这条商品对象中的的 number 值
data[index].number++;
} else if (type == 2) {
if (data[index].number > 0) {
// 通过下标将对应的数据减 1
data[index].number--;
}
} else if (type == 3) {
data.splice(index, 1);
// render(data);
console.log(document.getElementById('bigAlert'));
showAlert('删除成功', document.getElementById('bigAlert'));
}
// 调用渲染函数让页面更新
render(data);
}
6.编辑新增的保存按钮事件
type
:表示操作类型,1 表示编辑商品,2 表示添加商品。
编辑商品逻辑(type == 1
):获取编辑输入框中的值,并进行合法性验证,若验证不通过则调用 showAlert
函数显示错误提示框并返回。遍历 data
数组,找到要编辑的商品并更新其信息。隐藏编辑输入框区域,重新渲染页面,并显示编辑成功的提示框。
添加商品逻辑(type == 2
):获取添加输入框中的值,并进行合法性验证,若验证不通过则调用 showAlert
函数显示错误提示框并返回。创建一个新的商品对象,包含商品名称、价格、数量和新的 id
。将新商品对象添加到 data
数组中,隐藏添加输入框区域,重新渲染页面,并显示添加成功的提示框。
// 编辑/新增的保存按钮点击事件(功能已合并 ok)
function saveBtn(type) {
if (type == 1) {
// 获取输入框修改后的值,并去除两端空白字符,得到有效的输入内容
let productName = document.getElementById('productNameChange').value.trim();
let productPrice = document.getElementById('productPriceChange').value.trim();
let productNum = document.getElementById('productNumChange').value.trim();
// 验证商品名不能为空
if (productName === "") {
showAlert('商品名不能为空', document.getElementById('bigAlertErrorName'));
return;
}
// 验证价格是否为数字
if (isNaN(productPrice)) {
showAlert('请输入合法的数字', document.getElementById('bigAlertErrorName'));
return;
}
// 验证数量是否为合法数字
if (productNum % 1 !== 0) {
showAlert('请输入整数数量', document.getElementById('bigAlertErrorName'));
return;
}
// 遍历整个 data 数组,通过比较商品的唯一 id 找到当前正在编辑的商品的对应的数据
//(这样是为了知道正在编辑哪条数据,并且是唯一的 不会出错)
for (let i in data) {
// 如果被编辑的商品 id 等于 data 数据里的 id
if (data[i].id == productId) {
// 就把编辑好的数据赋给 data 里的数据
data[i].name = productName;
data[i].price = productPrice;
data[i].number = productNum;
}
}
// 把编辑输入框区域隐藏
document.getElementsByClassName('hiddenTextChange')[0].style.display = 'none';
// 重新渲染页面,更新展示信息
render(data);
// 调用 showAlert 显示操作成功提示框,传递提示文字消息,和对应的提示框元素(操作成功提示框)
showAlert('编辑成功', document.getElementById('bigAlert'));
} else if (type == 2) {
// 获取该输入框中用户输入的值
let productName = document.getElementById('productName').value;
let productPrice = document.getElementById('productPrice').value;
let productNum = document.getElementById('productNum').value;
// 商品名不能为空
if (productName === "") {
showAlert('商品名不能为空', document.getElementById('bigAlertErrorName'));
return;
}
// 验证价格是否为数字
if (isNaN(productPrice)) {
showAlert('请输入合法的数字', document.getElementById('bigAlertErrorName'));
return;
}
// 验证数量是否为合法数字
if (productNum % 1 !== 0) {
showAlert('请输入整数数量', document.getElementById('bigAlertErrorName'));
return;
}
// 要添加的新商品
let newProduct = {
name: productName,
// 价格可能是小数
price: parseFloat(productPrice),
// 数量是整数
number: parseInt(productNum),
id: ++maxId
};
// 把新的商品插入到 data 里
data.push(newProduct);
// 重新渲染页面
// 隐藏添加输入框区域
// cancelBtn();
// 显示操作成功提示框
showAlert('添加成功', document.getElementById('bigAlert'));
}
render(data);
}
7.编辑和新增按钮函数
index
:表示要编辑的商品在 data
数组中的下标。type
:表示操作类型,1 表示新增商品,2 表示编辑商品。
新增商品逻辑(type == 1
):显示新增商品的输入框区域,并清空输入框中的内容。
编辑商品逻辑(type == 2
):获取要编辑的商品的 id
,并将该商品的信息显示在编辑输入框中。显示编辑商品的输入框区域。
// 编辑和新增按钮的函数(已合并)
function editAddBtn(index, type) {
if (type == 1) {
// 新增按钮的函数没反应,但是把上面的 index 删了就可以了,但是编辑就不好用了(解决了 给没有下标的调用传一个 0 就行(0.1))
// 点击按钮后把隐藏起来的内容显示出来
document.getElementsByClassName('hiddenText')[0].style.display = 'block';
// 清空上一次输入的值(令他们为空的 字符串)
let productName = document.getElementById('productName');
let productPrice = document.getElementById('productPrice');
let productNum = document.getElementById('productNum');
productName.value = "";
productPrice.value = "";
productNum.value = "";
} else if (type == 2) {
// 获取当前编辑商品在 data 数组中对应的 id
productId = data[index].id;
// 把当前被编辑商品的内容显示在框里(方便用户在原基础上更改)
document.getElementById('productNameChange').value = data[index].name;
document.getElementById('productPriceChange').value = data[index].price;
document.getElementById('productNumChange').value = data[index].number;
// 把框显示出来,让用户能看到,方便编辑
document.getElementsByClassName('hiddenTextChange')[0].style.display = 'block';
}
}
8.操作提示框
message
:要显示的提示信息。element
:提示框的 DOM 元素。
逻辑说明:将提示信息插入到提示框的 span
元素中。显示提示框。使用 setTimeout
函数在 1.5 秒后隐藏提示框。
// 操作成功提示框的函数
function showAlert(message, element) {
let spanElement = element.children[0].children[1];
spanElement.innerHTML = `
${message}
`;
element.style.display = 'block';
setTimeout(function() {
element.style.display = 'none';
}, 1500);
}