#原生JS 实现可编辑表格

1,624 阅读8分钟

原生JS 实现可编辑表格

如下图所示,是本人和小组成员共同完成的可编辑表格:

image.png

关于设计

对于表格的想象,加上大学环境的渲染,期末成绩分数表格是一个很不错且适合的主题。每一个学生的成绩都可以修改,并且对总分进行累加,同时要设置哪一些是用户可以进行编辑的。

比如一个很简单的逻辑,教师和学生只能看见学号或者姓名这些基础信息,而且学生和学号一一匹配,不可修改,所以涉及到的可以编辑的区域就只有成绩;包括总分区域也是不可编辑的,如果可以就会破坏这个表格的使用规则。

其次,当你输入的数据,不符合当前的要求,比如小数,非数值,空值,负数或者大于当前最高分的数值等等,都是该表格所不能输入的数据。对于这些数据要给用户一个提示反馈,让他们知道这些是在表格中不符合规范的数据。

最后是删除功能,如果我需要删除一个学生的信息,那么这个删除的按键还需要让当前学生行直接删除,实现学生的信息删除。

关于实现

实现这个表格的思路:

1.首先是需要有一个Json文件,用于存储学生信息和成绩

2.要进行模块化,HTML只写DOM元素,CSS只写样式,JS只写逻辑。

3.梳理思路,首先肯定是要写出大体的表格框架,就是DOM元素,写出表格头和表格体,并给一个渲染的数据容器tbody。

4.进行一些基础设置,设置一些变量和常量,并且DOM操作获取当前元素。

image.png

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页面设计与开发思路和方法

根据查阅的资料和自己的总结:

  1. 数据驱动:数据是Web页面设计和开发的核心,需要从数据的角度出发,分析和处理数据,实现数据的可视化和交互。
  2. 用户体验:Web页面的用户体验是设计和开发的重要目标,需要考虑用户的需求和行为,提供简洁、直观、易用的界面和功能。
  3. 可维护性:Web页面的可维护性是设计和开发的重要考虑因素,需要采用合理的代码结构和规范,保证代码的可读性和可维护性。
  4. 安全性: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的学习历程也就告一段落了,感谢我的老师对我的指导。

欢迎大佬指点。

往事暗沉不可追,来日之路光灿烂。