设计思路
设计可编辑数据表格,表格可编辑的内容部分单机可以生成一个输入框,并且自动获取输入框焦点。在输入框输入修改内容之后,判断输入框的内容是否满足表格输入条件;若不满足则弹出错误提示框提示用户重新输入内容,若满足则用户单击空白处即可对更改的内容进行修改和保存。其中表格的数据来自外部的json,通过json数据生成可编辑表格。根据实际情况,表格没有新增数据功能。表格的可编辑列,计算的列,每列的数据大小,以及是否删除都可进行配置,在修改单元格内容和删除行数据都会映射到相应数据集中。对数据修改和保存后,对应的表格的总分列也会通过重新计算在表格中更新并保存。
实现方法
写好基本样式和框架,并且把表格需要的数据写在json文件中,通过Ajax获取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();
}
};
定义getHTML方法,先取出每一行的数据,对于表格的表头,通过Object.values()直接取出数据,并通过模板字符串直接渲染到页面中。对于表格内容,通过Object.keys()获取每行数据的键名数组,先定义一个temp_grade并赋值<tr>
,通过for in 获取下标并取出每一个键名,判断当前索引值是否等于键名数组的长度减一,若满足条件,则temp_grade加等于td并拼接上</tr>
,反之,直接等于td。然后把temp_grade插入到HTML中。
function getHtml() {
let titlekey, gradekey;
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>`;
for (let j in gradekey) {
let k = gradekey[j];
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();
actionBar();
}
定义totalScoreBar方法用于在thead里添加总分这一列,并通过setAttribute给它设置rname属性值为allgrade。
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");
}
}
定义actionBar方法用于在thead里添加操作栏这一列。并在每个tr里添加button标签。
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();
}
定义setEditable方法,用于设置哪些可编辑。传入一个数组arr表示可编辑的单元格列。通过for循环先获取表格行和列,再通过arr.forEach()和setAttribute方法给arr元素表示的那一列单元格设置name为editable。
function setEditable(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("name", "editable");
});
}
setCellCilck();
}
定义setAllScore方法,用于设置哪些可以计算分数。传入一个数组arr表示可计算的单元格列。通过for循环先获取表格行和列,再通过arr.forEach()和setAttribute方法给arr元素表示的那一列单元格设置class为grade。
function setAllScore(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");
});
}
}
定义一个updateScore方法,用来计算分数。通过class取出每行的分数的值,再取出总成绩的值。每个人总成绩等于每行分数相加。
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;
for (item of grade_data) {
for (let i = 0; i < item.length; i++) {
let gradekey = Object.keys(item[i]);
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;
}
}
定义一个addAnimate方法,表示单元格输入错误时的动画提示。
function addAnimate() {
thetips.className = "err movedown";
}
定义一个delRow方法,用于表示删除单元格行操作。通过rowindex获取当前行,再定义一个delindex变量,把rowindex-1的值赋给它。然后通过deleteRow(rowindex)删除行。然后取出当前单元格所在行的id为ediId,遍历原始数据。然后取出的当前单元格对应的数据的下标,然后判断ediId是否等于原数组的id,若等于,则删除当前行的数据。
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;
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);
}
}
}
};
}
}
定义setCellCilck方法,用于给class为grade的单元格添加点击事件。在里面定义一个scorearr数组,用于存放各科目的满分成绩。并将该数组传入到updateCell里面。
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();
};
}
}
定义updateCell方法,用于更新单元格的内容。传入ele和定义好的scorearr,表示当前点击的单元格和各个科目满分的数组。先取出当前ele的cellIndex,获取对应的满分数据。然后获取旧的单元格数据并保存为oldhtml。然后创建一个input标签,并传入oldhtml。在input标签的聚焦事件中判断输入的input值是否合法,若不合法,则调用addAnimate方法,弹出error标签的错误提示信息,若合法,则保存当前的值。然后取出当前单元格所在行的id为ediId,遍历原始数据并通过Object.keys()取出键数组。然后取出的当前单元格所在行的列数,然后判断ediId是否等于原数组的id,若等于,则将新的值赋给它,从而实现原数组的修改。
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;
for (item of grade_data) {
for (let i = 0; i < item.length; i++) {
let gradekey = Object.keys(item[i]);
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;
}
}
实现效果
对于页面开发不仅要实现基础的功能应用,还要实现更好更便捷的操作。页面的美观,用户的良好体验也至关重要,满足更好的性能。