1. 任务要求
- JSON数据,表格中数据来自服务端,由JSON格式表示。
- 通过JSON数据生成可编辑表格,并且灵活配置可编辑得到数据列。
- 输入数据时打开开发者模式有提示,并且设置判断,要求输入正确的成绩。
- 要求表格的可编辑列,计算的列,每列的数据大小,以及是否删除都可进行配置,在修改单元格内容和删除行数据都会映射到相应数据集中。
- 静态原型:
2. JS改进思路
- 利用AJAX读取本地json数据,获取学生栏数据和成绩栏数据。
- 定义一个delRow方法,用于表示删除单元格行操作。通过rowindex获取当前行,再定义一个delindex变量,把rowindex-1的值赋给它。然后通过deleteRow(rowindex)删除行。然后取出当前单元格所在行的id为ediId,遍历原始数据。然后取出的当前单元格对应的数据的下标,然后判断ediId是否等于原数组的id,若等于,则删除当前行的数据。
- 首先添加总分栏totalScoreBar方法并通过setAttribute给它设置rname属性值为allgrade。,updateScore方法更新总成绩通过class取出每行的分数的值,再取出总成绩的值。每个人总成绩等于每行分数相加。
3. 逻辑思路
- 先读取本地json数据,获取学生信息栏和成绩栏数据。
- 添加总分栏、操作栏(删除操作);为了满足需求,需要表示出可编辑单元格列(可进行编辑);同时设置可计算分数表格列。
- 在可编辑表格里传入定义的数组,用于提示各科目满分,同时为成绩传入删除事件,满足可删除操作。
- 更新单元格内容,并将数组传入里面,传入更新后的成绩,并添加动画。
- 定义更新总成绩方法,通过class取出每行的分数的值,再取出总成绩的值。每个人总成绩等于每行分数相加。定义删除事件。
- 最后完成这些后,渲染Dom内容,存放对象的key,传入以下函数totalScoreBar() 生成总分栏,setAllScore()设置需要计算总分的学科,setEditable()设置可编辑单元格,
updateScore()更新总分,flag = true 删除栏,if (flag) actionBar()生成操作栏。
4. 具体实现
-
读取数据、添加总分栏
-
添加删除栏,并传入删除方法;同时设置单元格可编辑性。
添加删除表格行,删除行事件,实现对删除行的id进行获取,遍历数据对象,找到删除行id值对应的那一条数据,最后完成对数据列表进行删除。
-
更新表格内容
① 首先给单元格添加点击事件在里面定义一个scorearr数组,用于存放各科目的满分成绩。并将该数组传入到updateCell里面。然后取出当前单元格所在行的id为ediId,遍历原始数据并通过Object.keys()取出键数组。然后取出的当前单元格所在行的列数,然后判断ediId是否等于原数组的id,若等于,则将新的值赋给它,从而实现原数组的修改。
② 定义updateCell方法,用于更新单元格的内容。传入ele和定义好的scorearr,表示当前点击的单元格和各个科目满分的数组。在input标签的聚焦事件中判断输入的input值是否合法,若不合法,则调用addAnimate方法,弹出error标签的错误提示信息,若合法,则保存当前的值。
③ 添加动画,并设置计数器,完成提示框消失,用于提醒用户编辑内容是否可行。
5. 页面展示
- 输入错误提示
6. 全部代码
- table.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>table</title>
</head>
<link rel="stylesheet" href="table.css">
<body>
<div id="tableBox">
<h2 class="title">可编辑表格</h2>
<div class="err">请输入0~100以内的数字</div>
<table class="table">
<thead>
<tr></tr>
</thead>
<tbody></tbody>
</table>
</div>
</body>
<script src="table.js"></script>
</html>
- table.css
* {
margin: 0;
padding: 0;
/* --border: 2px solid rgba(121, 121, 121, 1); */
}
#tableBox {
position: relative;
user-select: none;
}
.table {
margin: 0 auto;
border-spacing: 0;
border-collapse: collapse;
text-align: center;
margin-top: 47px;
z-index: 1;
}
.err {
display: none;
top: 95px;
width: 160px;
position: absolute;
margin-left: -100px;
left: 50%;
text-align: center;
padding: 15px 18px;
background: rgb(255 193 7 / 45%);
border-radius: 20px;
font-size: 13px;
font-weight: 600;
transition: top 1s;
z-index: -1;
}
.movedown {
top: 95px;
animation: movedown 3s;
}
@keyframes movedown {
0% {
top: 95px;
}
50% {
top: 48px;
}
100% {
top: 95px;
}
}
.title {
text-align: center;
padding: 8px 0;
}
tr,
td,
th {
border: var(--border);
}
th {
/* font-weight: 600; */
text-align: center;
background-color: #9e9e9e70;
}
td > input {
width: 100px;
height: 45px;
border: none;
font-size: 20px;
}
.table > thead > tr > th,
.table > tbody > tr > td {
width: 174px;
height: 47px;
font-size: 20px;
}
.table > thead > tr {
font-family: "宋体";
}
button {
color: #fff;
background-color: #d9534f;
border-color: #d43f3a;
user-select: none;
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
padding: 10px 12px;
font-size: 14px;
text-align: center;
}
- table.js
let stutable = document.getElementsByClassName("table")[0];
let stutable_title = stutable
.getElementsByTagName("thead")[0]
.getElementsByTagName("tr")[0]; // 获取th标题行
let stutable_grade = stutable.getElementsByTagName("tbody")[0]; // 获取tbody
let stu_trs = stutable_grade.getElementsByTagName("tr"); // 获取tbody的tr标签
var title_data = []; // 存放标题数据
var grade_data = []; // 存放成绩数据
let delbtns = document.getElementsByTagName("button");
var editcell = document.getElementsByName("editable"); // 可编辑的单元格
var grades = document.getElementsByClassName("grade"); // 需要计算的单元格
var thetips = document.getElementsByClassName("err")[0];
var alltr = document.getElementsByTagName("tr"); // 获取HTML中所有的tr标签
var flag; // 设置是否显示删除栏
// 读取本地json数据
let ajax = new XMLHttpRequest();
ajax.open("get", "data.json");
ajax.send();
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
let alldata = JSON.parse(ajax.responseText);
title_data.push(alldata.t_title); // 学生信息栏数据
grade_data.push(alldata.t_grades); // 学生成绩栏数据
getHtml(); // 生成HTML
}
};
// 渲染DOM内容
function getHtml() {
let titlekey, gradekey; // 用于存放对象的key
// 表头
for (item of title_data) {
for (let i = 0; i < item.length; i++) {
titlekey = Object.values(item[i]); // 取出表头数据
let temp_title = `
<th>${titlekey}</th>
`;
stutable_title.insertAdjacentHTML("beforeend", temp_title);
}
}
// 表格内容
for (item of grade_data) {
for (let i = 0; i < item.length; i++) {
gradekey = Object.keys(item[i]);
let temp_grade = `<tr>`;
// console.log(gradekey); // 获取到的key数组
for (let j in gradekey) {
let k = gradekey[j]; // 获取到的key值
// console.log(item[i][k]); // 取出对象中的值
// 判断是否是最后一个键名
if (j == gradekey.length - 1) {
temp_grade += `<td>${item[i][k]}</td></tr>`;
} else {
temp_grade += `<td>${item[i][k]}</td>`;
}
}
stutable_grade.insertAdjacentHTML("beforeend", temp_grade);
}
}
totalScoreBar(); // 生成总分栏
setAllScore([2, 3, 4, 5]); // 设置需要计算总分的学科
setEditable([2, 3, 4, 5]); // 设置可编辑单元格
updateScore(); // 更新总分
flag = true; // 删除栏
if (flag) actionBar(); // 生成操作栏,
}
// 添加总分栏
function totalScoreBar() {
let allscore = document.createElement("th");
allscore.innerText = "总分";
stutable_title.appendChild(allscore);
for (let j = 0; j < stu_trs.length; j++) {
let score = document.createElement("td");
score.innerText = "0";
stu_trs[j].appendChild(score);
score.setAttribute("rname", "allgrade");
}
}
// 添加操作栏
function actionBar() {
let caozuo = document.createElement("th");
caozuo.innerText = "操作";
stutable_title.appendChild(caozuo);
for (let k = 0; k < stu_trs.length; k++) {
let caozuo2 = document.createElement("td");
let btn = document.createElement("button");
btn.innerText = "删除";
caozuo2.appendChild(btn);
stu_trs[k].appendChild(caozuo2);
}
delRow(); // 删除操作
}
// 设置哪些单元格可编辑
function setEditable(arr) {
//arr 表示可编辑的单元格
// editable 设置单元格可编辑性
var strow = stutable.rows.length; // 获取表格行数
for (let i = 1; i < strow; i++) {
let stcell = stutable.rows[i].cells; // 获取表格列数
// console.log(stcell);
arr.forEach(function (item) {
stcell[item].setAttribute("name", "editable");
});
}
setCellCilck();
}
// 设置可计算分数的表格列
function setAllScore(arr) {
// arr 表示需要计算总分的单元格
var strow = stutable.rows.length; // 获取表格行数
for (let i = 1; i < strow; i++) {
let stcell = stutable.rows[i].cells; // 获取表格列数
arr.forEach(function (item) {
stcell[item].setAttribute("class", "grade");
});
}
}
// 给单元格添加点击事件
function setCellCilck() {
let scorearr = [100, 100, 100, 100, 100, 100, 100]; // 设计单科成绩的满分
for (let i = 0; i < editcell.length; i++) {
editcell[i].onclick = function () {
updateCell(this, scorearr);
delRow();
};
}
}
// 更新单元格内容
function updateCell(ele, scorearr) {
let scoreMax = scorearr[ele.cellIndex - 2];
scoreMax = scoreMax || 100;
console.log("当前科目的满分是:" + scoreMax);
if (document.getElementsByClassName("active-input").length == 0) {
var oldhtml = ele.innerHTML;
ele.innerHTML = "";
var newInput = document.createElement("input");
newInput.setAttribute("class", "active-input");
newInput.value = oldhtml;
newInput.onblur = function () {
this.value = parseFloat(this.value);
if (this.value < 0 || this.value > scoreMax) {
console.log("err");
addAnimate();
thetips.style.display = "block";
return;
} else {
thetips.style.display = "none";
ele.innerHTML = this.value == oldhtml ? oldhtml : this.value;
/* 映射数据表 */
// 取出当前单元格数据
let ediId = ele.parentNode.children[0].innerHTML; // 获取当前修改的单元格的id
// console.log(ediId);
for (item of grade_data) {
for (let i = 0; i < item.length; i++) {
//获取要修改数据中的所有keys值数组;
// 取出数据集的所有键名
let gradekey = Object.keys(item[i]);
// console.log(item[i].id); // 取出数据集的id
// console.log(ele.cellIndex); // 输出当前单元格所在行的列数
//找到id值对应的那一条数据
if (item[i].id == ediId) {
//根据keys数组的索引对对象数据进行修改
item[i][gradekey[ele.cellIndex]] = parseFloat(this.value);
}
}
}
console.log("修改后的数据是:", grade_data);
updateScore();
}
};
newInput.select();
ele.appendChild(newInput);
newInput.focus();
} else {
return;
}
}
// 添加动画
function addAnimate() {
thetips.className = "err movedown";
setTimeout(function () {
document.getElementsByClassName("err")[0].style.opacity = 0;
}, 1500);
}
// 更新总成绩
function updateScore() {
// console.log(grades);
for (let n = 1; n < alltr.length; n++) {
var grade01 =
grades[n].parentNode.parentNode.children[n - 1].querySelectorAll(
"td[class]"
);
var grade02 =
grades[n].parentNode.parentNode.children[n - 1].querySelectorAll(
"td[rname]"
);
var sum = 0;
for (let i = 0; i < grade01.length; i++) {
sum += parseFloat(grade01[i].innerHTML);
for (let j = 0; j < grade02.length; j++) {
grade02[j].innerHTML = sum;
}
}
}
}
// 删除表格行
//删除行事件
function delRow() {
for (let i = 0; i < delbtns.length; i++) {
delbtns[i].onclick = function () {
let rowindex = this.parentNode.parentNode.rowIndex; // 获取当前行
let delindex = rowindex - 1;
stutable.deleteRow(rowindex);
/* 映射数据表 */
//对删除行的id进行获取
let ediId = this.parentNode.parentNode.children[0].innerHTML; // 获取当前单元格的id
//遍历数据对象
for (item of grade_data) {
for (let i = 0; i < item.length; i++) {
//找到删除行id值对应的那一条数据
if (item[i].id == ediId) {
//对数据列表进行删除
item.splice(delindex, 1);
console.log("删除后的数据是:", grade_data);
}
}
}
};
}
}
- data.json
{
"t_title": [
{
"title_name": "学号"
},
{
"title_name": "姓名"
},
{
"title_name": "WEB前后端综合开发"
},
{
"title_name": "专业综合实战"
},
{
"title_name": "Vue.js"
},
{
"title_name": "WEB前端开发技术"
}
],
"t_grades": [
{
"id": 205020028,
"Student_name": "张三",
"Chinese_score": 98,
"Math_score": 80,
"English_score": 91,
"Political_score": 92
},
{
"id": 205020027,
"Student_name": "李四",
"Chinese_score": 80,
"Math_score": 80,
"English_score": 80,
"Political_score": 93
},
{
"id": 205020014,
"Student_name": "王五",
"Chinese_score": 90,
"Math_score": 80,
"English_score": 90,
"Political_score": 99
},
{
"id": 205020010,
"Student_name": "赵六",
"Chinese_score": 90,
"Math_score": 90,
"English_score": 90,
"Political_score": 88
},
{
"id": 205020032,
"Student_name": "小明",
"Chinese_score": 90,
"Math_score": 85,
"English_score": 85,
"Political_score": 88
},
{
"id": 205000001,
"Student_name": "小红",
"Chinese_score": 98,
"Math_score": 82,
"English_score": 96,
"Political_score": 92
}
]
}