1、前言

在普通的可编辑表格的基础上,改进可编辑表格。数据来自外部的json(模拟服务端),通过json数据生成可编辑表格。根据实际情况,表格没有新增数据功能。表格的可编辑列,计算的列,每列的数据大小,以及是否删除都可进行配置,在修改单元格内容和删除行数据都会映射到相应数据集中。

实现效果:

在这里插入图片描述

2、设计思路与方法

  • 现将基本样式写好,将所有的数据写在json文件里。定义两个空数组,通过Ajax将表格的标题和内容成绩读取并分别保存在数组title_data和grade_data里。

  • 定义getHTML方法,先取出每一行的数据,对于表格的表头,通过Object.values()直接取出数据,并通过模板字符串直接渲染到页面中。对于表格内容,通过Object.keys()获取每行数据的键名数组,先定义一个temp_grade并赋值<tr>,通过for in 获取下标并取出每一个键名,判断当前索引值是否等于键名数组的长度减一,若满足条件,则temp_grade加等于td并拼接上</tr>,反之,直接等于td。然后把temp_grade插入到HTML中。在最下方调用totalScoreBar(),actionBar(),setAllScore([2, 3, 4]),setEditable([2, 3, 4]),updateScore()方法,初始化整个表格。

  • 定义totalScoreBar方法用于在thead里添加总分这一列,并通过setAttribute给它设置rname属性值为allgrade。

  • 定义actionBar方法用于在thead里添加操作栏这一列。并在每个tr里添加button标签。

  • 定义flag开关,设置是否调用actionBar方法

  • 定义setEditable方法,用于设置哪些可编辑。传入一个数组arr表示可编辑的单元格列。通过for循环先获取表格行和列,再通过arr.forEach()和setAttribute方法给arr元素表示的那一列单元格设置name为editable。

  • 定义setAllScore方法,用于设置哪些可以计算分数。传入一个数组arr表示可计算的单元格列。通过for循环先获取表格行和列,再通过arr.forEach()和setAttribute方法给arr元素表示的那一列单元格设置class为grade。

  • 定义一个updateScore方法,用来计算分数。通过class取出每行的分数的值,再取出总成绩的值。每个人总成绩等于每行分数相加。

  • 定义一个addAnimate方法,表示单元格输入错误时的动画提示。

  • 定义一个delRow方法,用于表示删除单元格行操作。通过rowindex获取当前行,再定义一个delindex变量,把rowindex-1的值赋给它。然后通过deleteRow(rowindex)删除行。然后取出当前单元格所在行的id为ediId,遍历原始数据。然后取出的当前单元格对应的数据的下标,然后判断ediId是否等于原数组的id,若等于,则删除当前行的数据。

  • 定义setCellCilck方法,用于给class为grade的单元格添加点击事件。在里面定义一个scorearr数组,用于存放各科目的满分成绩。并将该数组传入到updateCell里面。

  • 定义updateCell方法,用于更新单元格的内容。传入ele和定义好的scorearr,表示当前点击的单元格和各个科目满分的数组。先取出当前ele的cellIndex,获取对应的满分数据。然后获取旧的单元格数据并保存为oldhtml。然后创建一个input标签,并传入oldhtml。在input标签的聚焦事件中判断输入的input值是否合法,若不合法,则调用addAnimate方法,弹出error标签的错误提示信息,若合法,则保存当前的值。然后取出当前单元格所在行的id为ediId,遍历原始数据并通过Object.keys()取出键数组。然后取出的当前单元格所在行的列数,然后判断ediId是否等于原数组的id,若等于,则将新的值赋给它,从而实现原数组的修改。

3、源码展示

table.css

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
* {
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: orange;
border-radius: 5px;
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: rgba(204, 204, 204, 1);
}

td>input {
width: 100px;
height: 45px;
border: none;
font-size: 16px;
}


.table>thead>tr>th,
.table>tbody>tr>td {
width: 100px;
height: 45px;
font-size: 16px;
}

.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.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
<!DOCTYPE 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">成绩输入有误,请重新输入!</div>
<table class="table">
<thead>
<tr></tr>
</thead>
<tbody></tbody>
</table>
</div>
</body>
<script src="table.js"></script>

</html>

table.js

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

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', 'data2.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, 6, 7, 8]) // 设置需要计算总分的学科
setEditable([2, 3, 4, 5, 6, 7, 8]) // 设置可编辑单元格
updateScore() // 更新总分
flag = false; // 删除栏
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 = [150, 150, 150, 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++) {
// 取出数据集的所有键名
let gradekey = Object.keys(item[i]);
// console.log(item[i].id); // 取出数据集的id
// console.log(ele.cellIndex); // 输出当前单元格所在行的列数
if (item[i].id == ediId) {
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"
}

// 更新总成绩
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);
/* 映射数据表 */
let ediId = this.parentNode.parentNode.children[0].innerHTML; // 获取当前单元格的id
for (item of grade_data) {
for (let i = 0; i < item.length; i++) {
if (item[i].id == ediId) {
item.splice(delindex, 1)
console.log("删除后的数据是:", grade_data);
}
}
}
}
}
}



data2.json

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
{
"t_title": [
{
"title_name": "学号"
},
{
"title_name": "姓名"
},
{
"title_name": "语文"
},
{
"title_name": "数学"
},
{
"title_name": "英语"
},
{
"title_name": "政治"
},
{
"title_name": "历史"
},
{
"title_name": "地理"
},
{
"title_name": "生物"
}
],
"t_grades": [
{
"id": 1101,
"Student_name": "小王",
"Chinese_score": 98,
"Math_score": 80,
"English_score": 91,
"Political_score":92,
"History_score":86,
"Geographic_score":88,
"Biological_score":96
},
{
"id": 1102,
"Student_name": "小曾",
"Chinese_score": 80,
"Math_score": 80,
"English_score": 80,
"Political_score":93,
"History_score":95,
"Geographic_score":86,
"Biological_score":89
},
{
"id": 1103,
"Student_name": "小赵",
"Chinese_score": 90,
"Math_score": 80,
"English_score": 90,
"Political_score":99,
"History_score":91,
"Geographic_score":98,
"Biological_score":95
},
{
"id": 1104,
"Student_name": "小丽",
"Chinese_score": 90,
"Math_score": 90,
"English_score": 90,
"Political_score":88,
"History_score":78,
"Geographic_score":89,
"Biological_score":100
},
{
"id": 1105,
"Student_name": "小明",
"Chinese_score": 90,
"Math_score": 85,
"English_score": 85,
"Political_score":88,
"History_score":89,
"Geographic_score":100,
"Biological_score":99
},
{
"id": 1106,
"Student_name": "小红",
"Chinese_score": 98,
"Math_score": 82,
"English_score": 96,
"Political_score":92,
"History_score":92,
"Geographic_score":90,
"Biological_score":92
}
]
}

可编辑表格的实现就到这里了,若有问题欢迎留言。