原生JS 实现可编辑表格
如下图所示,是本人和小组成员共同完成的可编辑表格:
关于设计
对于表格的想象,加上大学环境的渲染,期末成绩分数表格是一个很不错且适合的主题。每一个学生的成绩都可以修改,并且对总分进行累加,同时要设置哪一些是用户可以进行编辑的。
比如一个很简单的逻辑,教师和学生只能看见学号或者姓名这些基础信息,而且学生和学号一一匹配,不可修改,所以涉及到的可以编辑的区域就只有成绩;包括总分区域也是不可编辑的,如果可以就会破坏这个表格的使用规则。
其次,当你输入的数据,不符合当前的要求,比如小数,非数值,空值,负数或者大于当前最高分的数值等等,都是该表格所不能输入的数据。对于这些数据要给用户一个提示反馈,让他们知道这些是在表格中不符合规范的数据。
最后是删除功能,如果我需要删除一个学生的信息,那么这个删除的按键还需要让当前学生行直接删除,实现学生的信息删除。
关于实现
实现这个表格的思路:
1.首先是需要有一个Json文件,用于存储学生信息和成绩
2.要进行模块化,HTML只写DOM元素,CSS只写样式,JS只写逻辑。
3.梳理思路,首先肯定是要写出大体的表格框架,就是DOM元素,写出表格头和表格体,并给一个渲染的数据容器tbody。
4.进行一些基础设置,设置一些变量和常量,并且DOM操作获取当前元素。
5.设置函数来对表格进行设置,比如数据渲染,数据修改,编辑范围,删除等。
部分核心代码展示
// 读取本地json数据
async function fetchData() {
const response = await fetch('G7.json');
if (response.ok) {
const data = await response.json();
title_data.push(data.t_title);
grade_data.push(data.t_grades);
getHtml();
}
}
fetchData();
异步函数fetchData。它使用了ES6中的async/await语法来处理异步操作。在函数中,它首先使用fetch方法获取一个名为G7.json的文件。如果获取成功,它会将文件中的数据解析为JSON格式。
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) {
if(stcell[item]) {
stcell[item].setAttribute("name", "editable")
}
})
}
setCellCilck()
}
它接受一个数组作为参数,表示可编辑的单元格。函数中使用了一个for循环来遍历表格的每一行,然后使用一个forEach循环来遍历可编辑单元格的数组。如果当前单元格是可编辑的,就给它设置一个名为"editable"的属性。
function delRow() {
for (const btn of delbtns) {
btn.addEventListener("click", function() {
const row = this.parentNode.parentNode;
const rowIndex = row.rowIndex - 1;
stutable.deleteRow(rowIndex + 1);
const id = row.children[0].textContent;
grade_data.forEach(item => {
const index = item.findIndex(obj => obj.id === id);
if (index !== -1) {
item.splice(index, 1);
console.log("当前数据已删除");
}
});
});
}
}
该函数用于删除表格中的一行数据。函数中使用了一个for循环来遍历所有的删除按钮,然后为每个按钮添加了一个点击事件监听器。当用户点击某个删除按钮时,该事件监听器会执行一些操作,包括获取该按钮所在行的索引、从表格中删除该行、从数据数组中删除该行对应的数据等。
拓展思考基于数据的Web页面设计与开发思路和方法
根据查阅的资料和自己的总结:
- 数据驱动:数据是Web页面设计和开发的核心,需要从数据的角度出发,分析和处理数据,实现数据的可视化和交互。
- 用户体验:Web页面的用户体验是设计和开发的重要目标,需要考虑用户的需求和行为,提供简洁、直观、易用的界面和功能。
- 可维护性:Web页面的可维护性是设计和开发的重要考虑因素,需要采用合理的代码结构和规范,保证代码的可读性和可维护性。
- 安全性:Web页面的安全性是设计和开发的重要保障,需要采用合理的安全策略和技术,保护用户的隐私和数据安全。
基于数据的Web页面设计和开发需要采用一系列的技术和工具,包括数据分析和处理技术、前端开发技术、后端开发技术、数据库技术、安全技术等。其中,前端开发技术是设计和开发的重要组成部分,包括HTML、CSS、JavaScript等技术,用于实现Web页面的界面和交互。
同时还要考虑浏览器的优化,浏览器渲染,浏览器性能,浏览器存储和兼容性等等。还有不同屏幕尺寸的适应,对于不同状况的人的适应,加载和响应速度等性能优化。
后端开发技术则用于实现Web页面的业务逻辑和数据处理,包括PHP、Java、Python等技术。数据库技术则用于存储和管理Web页面的数据,包括MySQL、Oracle、MongoDB等技术。安全技术则用于保护Web页面的安全性,包括SSL、HTTPS、防火墙等技术。
综上所述,基于数据的Web页面设计和开发是一种以数据为核心的设计和开发方法,它需要遵循一系列的原则和最佳实践,采用一系列的技术和工具,实现Web页面的功能和价值。
最后附上可编辑表格的全部代码:
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">
<link rel="stylesheet" href="G7css.css">
<title>学生成绩表</title>
</head>
<body>
<div id="tableBox">
<h2 class="title">G7-学生成绩表</h2>
<div class="wrong" style="display: none;">当前为非数值,请重新输入</div>
<div class="wrong1" style="display: none;">超过最大输入,请重新输入</div>
<div class="wrong2" style="display: none;">低于最小输入,请重新输入</div>
<table class="table" >
<thead>
<tr>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</body>
<script src="G7.js"></script>
</html>
CSS
* {
margin: 0;
padding: 0;
--border: 5px solid rgb(248, 248, 248);
}
#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;
}
.wrong {
display: none;
top: 95px;
width: 300px;
position: absolute;
margin-left: -100px;
left: 47%;
text-align: center;
padding: 15px 18px;
background: rgb(255, 251, 34);
border-radius: 5px;
font-size: 20px;
font-weight: 600;
transition: top 1s;
z-index: -1;
}
.wrong1 {
display: none;
top: 95px;
width: 300px;
position: absolute;
margin-left: -100px;
left: 47%;
text-align: center;
padding: 15px 18px;
background: rgb(255, 70, 70);
border-radius: 5px;
font-size: 20px;
font-weight: 600;
transition: top 1s;
z-index: -1;
}
.wrong2 {
display: none;
top: 95px;
width: 300px;
position: absolute;
margin-left: -100px;
left: 47%;
text-align: center;
padding: 15px 18px;
color: #fff;
background: rgb(160, 19, 19);
border-radius: 5px;
font-size: 20px;
font-weight: 600;
transition: top 1s;
z-index: -1;
}
.movedown {
top: 80px;
animation: moveup 3s infinite;
}
@keyframes moveup {
0% {
top: 95px
}
50% {
top: 48px
}
100% {
top: 48px
}
}
.title {
text-align: center;
padding: 8px 0;
}
tr,
td,
th {
border: var(--border)
}
th {
font-weight: 600;
text-align: center;
background-color: rgb(124, 198, 255);
}
td>input {
width: 120px;
height: 45px;
border: none;
font-size: 20px;
}
.table>thead>tr>th,
.table>tbody>tr>td {
width: 130px;
height: 45px;
font-size: 16px;
}
.table>thead>tr {
font-family: '微软雅黑';
font-weight: bolder ;
}
button {
color: #fff;
width: 130px;
height: 50px;
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;
}
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 wrongtips = document.getElementsByClassName("wrong")[0]
var wrongtips1 = document.getElementsByClassName("wrong1")[0]
var wrongtips2 = document.getElementsByClassName("wrong2")[0]
var alltr = document.getElementsByTagName("tr") // 获取HTML中所有的tr标签
var flag; // 设置是否显示删除栏
// 读取本地json数据
async function fetchData() {
const response = await fetch('G7.json');
if (response.ok) {
const data = await response.json();
title_data.push(data.t_title);
grade_data.push(data.t_grades);
getHtml();
}
}
fetchData();
// 渲染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) {
if(stcell[item]) {
stcell[item].setAttribute("name", "editable")
}
})
}
setCellCilck()
}
// 设置可计算分数的表格列
function setAllScore(arr) {
const stcells = Array.from(stutable.querySelectorAll('td'));
stcells.forEach((cell) => {
if (arr.includes(cell.cellIndex)) {
cell.classList.add('grade');
}
});
}
// 给单元格添加点击事件
function setCellCilck() {
let scorearr = [100, 100, 100, 100] // 设计单科成绩的满分
for (let i = 0; i < editcell.length; i++) {
editcell[i].onclick = function () {
updateCell(this, scorearr)
// delRow()
}
}
}
// 更新单元格内容
function updateCell(upt, scorearr) {
let scoreMax = scorearr[upt.cellIndex - 2]
scoreMax = scoreMax || 100
console.log('当前科目的满分是:' + scoreMax);
if (document.getElementsByClassName("active-input").length == 0) {
const oriScript = upt.innerHTML;
upt.innerHTML = '';
const newInput = document.createElement('input');
newInput.classList.add("active-input");
newInput.value = oriScript;
newInput.onblur = function () {
switch (true) {
case (this.value === "" || isNaN(this.value)):
console.log("wrong");
wrongError();
wrongtips.style.display = "block";
wrongtips1.style.display = "none";
wrongtips2.style.display = "none";
return;
case (this.value > 100):
console.log("wrong1");
wrongError2();
wrongtips1.style.display = "block";
wrongtips.style.display = "none";
wrongtips2.style.display = "none";
return;
case (this.value < 0):
console.log("wrong2");
wrongError3();
wrongtips2.style.display = "block";
wrongtips.style.display = "none";
wrongtips1.style.display = "none";
return;
default:
wrongtips.style.display = "none";
wrongtips1.style.display = "none";
wrongtips2.style.display = "none";
// 设置一个整数和小数的判断,如果大于0.5,就向上取证,如果小于就保持原样
var num = parseFloat(this.value);
var intNum = parseInt(num);
var decimal = num - intNum;
if (decimal > 0.4) {
intNum += 1;
}
// upt.innerHTML = this.value == oriScript ? oriScript : intNum;
if (this.value == oriScript) {
upt.innerHTML = oriScript;
} else {
upt.innerHTML = intNum;
}
updateScore()
break;
}
}
newInput.select()
upt.appendChild(newInput);
newInput.focus()
} else {
return
}
}
// 添加动画
function wrongError() {
wrongtips.className = "wrong movedown"
}
function wrongError2() {
wrongtips1.className = "wrong1 movedown"
}
function wrongError3() {
wrongtips2.className = "wrong2 movedown"
}
// 更新总成绩
function updateScore() {
for (let n = 1; n < alltr.length; n++) {
const row = alltr[n];
const numSum = Array.from(row.querySelectorAll('td[name]'));
const numSum1 = Array.from(row.querySelectorAll('td[rname]'));
let sum = numSum.reduce((add, td) => add + parseFloat(td.innerHTML), 0);
numSum1.forEach(td => td.innerHTML = sum);
}
}
// 删除表格行
function delRow() {
for (const btn of delbtns) {
btn.addEventListener("click", function() {
const row = this.parentNode.parentNode;
const rowIndex = row.rowIndex - 1;
stutable.deleteRow(rowIndex + 1);
const id = row.children[0].textContent;
grade_data.forEach(item => {
const index = item.findIndex(obj => obj.id === id);
if (index !== -1) {
item.splice(index, 1);
console.log("当前数据已删除");
}
});
});
}
}
JSON
{
"t_title": [
{
"title_name": "学号"
},
{
"title_name": "姓名"
},
{
"title_name": "前端综合开发"
},
{
"title_name": "专业项目实战"
},
{
"title_name": "Web前端开发技术"
},
{
"title_name": "HTML5应用开发"
}
],
"t_grades": [
{
"id": 2201,
"Student_name": "彭姐",
"web_ES6": 95,
"web_HTTP": 99,
"web_max": 96,
"HTML":97
},
{
"id": 2202,
"Student_name": "霞指导",
"web_ES6": 92,
"web_HTTP": 80,
"web_max": 96,
"HTML":93
},
{
"id": 2203,
"Student_name": "老马",
"web_ES6": 80,
"web_HTTP": 80,
"web_max": 81,
"HTML":100
},
{
"id": 2204,
"Student_name": "JO科长",
"web_ES6": 80,
"web_HTTP": 80,
"web_max": 80,
"HTML":80
},
{
"id": 2205,
"Student_name": "琦总监",
"web_ES6": 81,
"web_HTTP": 81,
"web_max": 81,
"HTML":81
}
]
}
至此ES6的学习历程也就告一段落了,感谢我的老师对我的指导。
欢迎大佬指点。
往事暗沉不可追,来日之路光灿烂。