JavaScript高阶项目—组件化的可编辑表格

news2024/9/25 19:23:25

1. 任务要求

  1. JSON数据,表格中数据来自服务端,由JSON格式表示。
  2. 通过JSON数据生成可编辑表格,并且灵活配置可编辑得到数据列。
  3. 输入数据时打开开发者模式有提示,并且设置判断,要求输入正确的成绩。
  4. 要求表格的可编辑列,计算的列,每列的数据大小,以及是否删除都可进行配置,在修改单元格内容和删除行数据都会映射到相应数据集中。
  5. 静态原型:
    0010

2. JS改进思路

  1. 利用AJAX读取本地json数据,获取学生栏数据和成绩栏数据。
  2. 定义一个delRow方法,用于表示删除单元格行操作。通过rowindex获取当前行,再定义一个delindex变量,把rowindex-1的值赋给它。然后通过deleteRow(rowindex)删除行。然后取出当前单元格所在行的id为ediId,遍历原始数据。然后取出的当前单元格对应的数据的下标,然后判断ediId是否等于原数组的id,若等于,则删除当前行的数据。
  3. 首先添加总分栏totalScoreBar方法并通过setAttribute给它设置rname属性值为allgrade。,updateScore方法更新总成绩通过class取出每行的分数的值,再取出总成绩的值。每个人总成绩等于每行分数相加。

3. 逻辑思路

  1. 先读取本地json数据,获取学生信息栏和成绩栏数据。
  2. 添加总分栏、操作栏(删除操作);为了满足需求,需要表示出可编辑单元格列(可进行编辑);同时设置可计算分数表格列。
  3. 在可编辑表格里传入定义的数组,用于提示各科目满分,同时为成绩传入删除事件,满足可删除操作。
  4. 更新单元格内容,并将数组传入里面,传入更新后的成绩,并添加动画。
  5. 定义更新总成绩方法,通过class取出每行的分数的值,再取出总成绩的值。每个人总成绩等于每行分数相加。定义删除事件。
  6. 最后完成这些后,渲染Dom内容,存放对象的key,传入以下函数totalScoreBar() 生成总分栏,setAllScore()设置需要计算总分的学科,setEditable()设置可编辑单元格,
    updateScore()更新总分,flag = true 删除栏,if (flag) actionBar()生成操作栏。

4. 具体实现

  • 读取数据、添加总分栏
    001
    002

  • 添加删除栏,并传入删除方法;同时设置单元格可编辑性。
    003
    004
    添加删除表格行,删除行事件,实现对删除行的id进行获取,遍历数据对象,找到删除行id值对应的那一条数据,最后完成对数据列表进行删除。
    007

  • 更新表格内容
    ① 首先给单元格添加点击事件在里面定义一个scorearr数组,用于存放各科目的满分成绩。并将该数组传入到updateCell里面。然后取出当前单元格所在行的id为ediId,遍历原始数据并通过Object.keys()取出键数组。然后取出的当前单元格所在行的列数,然后判断ediId是否等于原数组的id,若等于,则将新的值赋给它,从而实现原数组的修改。
    005
    ② 定义updateCell方法,用于更新单元格的内容。传入ele和定义好的scorearr,表示当前点击的单元格和各个科目满分的数组。在input标签的聚焦事件中判断输入的input值是否合法,若不合法,则调用addAnimate方法,弹出error标签的错误提示信息,若合法,则保存当前的值。
    006
    ③ 添加动画,并设置计数器,完成提示框消失,用于提醒用户编辑内容是否可行。
    008

5. 页面展示

009

  • 输入错误提示
    011

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
    }
  ]
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/497874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新鲜热乎的春招面经汇总

作者&#xff1a;阿秀 校招八股文学习网站&#xff1a;https://interviewguide.cn 这是阿秀的第「263」篇原创 小伙伴们大家好&#xff0c;我是阿秀。 欢迎今年参加秋招的小伙伴加入阿秀的学习圈&#xff0c;目前已经超过 2300 小伙伴加入&#xff01;去年认真准备和走下来的基…

高精度DEM(12.5m)数据以及下载方式介绍

一、 DEM代表数字高程模型&#xff0c;是一种数字地形模型&#xff0c;用于表示地球表面的海拔高度。 DEM数据可以通过多种方法获得&#xff0c;包括激光雷达、遥感技术和GPS测量等。 DEM数据通常以栅格形式呈现&#xff0c;每个栅格单元包含一个高度值。DEM数据可以用于许多…

gitee如何上传自己的代码

1&#xff0c;右击你要上传的项目&#xff0c;选择Git bash Here 2,接着输入 git init 此时你会发现你的当前文件夹会多了一个.git文件夹 点击.git文件夹&#xff0c;进入之后发现是这样&#xff0c;然后用记事本打开config文件 3,发现是这样&#xff0c;然后我们要配置一个用…

简单科普视频云

1. 本文目的 每当我连写几篇行业分析的虚文以后&#xff0c;我都会做一两篇技术科普和产品分析的硬核分享&#xff0c;证明我是脚踏实地的戏说江姗&#xff0c;而非云里雾里的胡乱推倒。 其实是写硬核分享应者寥寥&#xff0c;反倒是写一些虚文很多人看热闹。 最近看到PPIO王闻…

数字化转型导师坚鹏:如何制定企业数字化转型年度培训规划

如何制定企业数字化转型年度培训规划 ——以推动企业数字化转型战略落地为核心&#xff0c;实现知行果合一 课程背景&#xff1a; 很多企业都在开展企业数字化转型培训工作&#xff0c;目前存在以下问题急需解决&#xff1a; 缺少针对性的企业数字化转型年度培训规划 不清…

双目视觉(七)稀疏双目匹配

系列文章 双目视觉&#xff08;一&#xff09;双目视觉系统双目视觉&#xff08;二&#xff09;双目匹配的困难和评判标准双目视觉&#xff08;三&#xff09;立体匹配算法双目视觉&#xff08;四&#xff09;匹配代价双目视觉&#xff08;五&#xff09;立体匹配算法之动态规…

测试1:概念

1.需求 用户需求:该需求一般比较简略。(一句话) 软件需求:或者功能需求,详细描述开发人员必须实现的软件功能(一个文档) 2.BUG 说明是存在的并且正确,程序和规格说明之间不匹配是错误 3.软件生命周期 从诞生到停服 需求分析,计划,设计,编码,测试,运行维护 需求分析:分析…

出海的中国企业,为什么有80%都选择了这家云服务商?

对于想要出海的中国企业来说&#xff0c;什么样的云服务才是他们的最佳选择&#xff1f; 中国企业出海驶入快车道 中国企业的出海大潮&#xff0c;多年来一直方兴未艾&#xff0c;并且得到了政府部门的大力支持。这股“走出去”的热潮&#xff0c;一方面对内有利于推动产业升级…

均摊时间复杂度

均摊时间复杂度&#xff0c;它对应的分析方法&#xff0c;摊还分析&#xff08;或者叫平摊分析&#xff09; 均摊时间复杂度应用的场景比它更加特殊、更加有限 // array表示一个长度为n的数组// 代码中的array.length就等于nint[] array new int[n];int count 0;void insert…

电子时钟实现

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 利用汇编语言实现一个可以在显示器上显示时、分、秒的电子时钟&#xff0c;并能提供设置闹钟时间、选择闹钟铃声功能。 问题描述 利用汇编语言实现一个可以在显示器上显示时、分、秒的电子时钟&#xff0c;并能提供设置闹钟时间…

【Go编程语言】 Go和Goland的安装

Go和Goland的安装 文章目录 Go和Goland的安装一、 Goland官网二、打开Goland&#xff0c;并输入激活码 GO官网地址&#xff1a;https://golang.google.cn/ go中文网地址&#xff1a;https://studygolang.com/dl 安装完成得到的目录 环境变量配置&#xff1a; 1.新建变量 变…

【Gin框架深度解析】路由实现原理,让你彻底掌握Gin中路由的奥秘!

文章目录 Gin路由1、基本路由2、Restful风格的API3、API参数4、URL参数5、表单参数6、上传单个文件6.1、上传特定文件 7、上传多个文件8、路由分组9、404页面处理10、路由原理 Gin路由 1、基本路由 ​ 举一个例子&#xff1a; package mainimport ("net/http"&quo…

ARM嵌入式编译器控制诊断信息(warning/error)

ARM编译器以警告&#xff08;warning&#xff09;和错误&#xff08;error&#xff09;的形式来提供编译诊断信息&#xff0c;并且用户可以通过一些命令行选项&#xff0c;来控制这些warnings和errors的打开或者关闭。编译器会在程序编译和链接过程中将遇到的warnings和errors在…

开关电源基础03:正激和反激开关电源拓扑(1)-正激拓扑

说在开头&#xff1a;关于薛定谔的波动方程&#xff08;2&#xff09; 全新的量子理论诞生不到一年&#xff0c;很快面临着粒子和波动的内战。矩阵力学从直接观测到的原子谱线出发&#xff0c;引入矩阵的数学工具&#xff0c;建立了整个新力学的大厦&#xff1b;它强调观测到的…

大数据Doris(十三):Duplicate数据模型以及聚合模型的局限性

文章目录 Duplicate数据模型以及聚合模型的局限性 一、Duplicate数据模型 二、聚合模型的局限性 Duplicate数据模型以及聚合模型的局限性 一、Duplicate数据模型 在某些多维分析场景下&#xff0c;数据既没有主键&#xff0c;也没有聚合需求&#xff0c;只需要将数据原封不…

JDK17新特性之--新的Compact Strings(JEP 254)

前言 JDK9将String底层的数据结构从private final char value[];改成了private final byte[] value; &#xff0c; JEP 254: Compact Strings(紧凑字符串)&#xff0c;这要修改的目的就是为了节省空间1。我们先看一下JDK9和JDK8中String源码的变化。 JDK9中String源码&#xf…

深度学习技巧应用12-神经网络训练中批归一化的应用

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用12-神经网络训练中批归一化的应用,在深度学习中,批归一化(Batch Normalization,简称BN)是一种重要的技巧,它在许多神经网络中都得到了广泛应用。本文将详细介绍批归一化的原理和应用,并结合PyTorch框架构建一个简…

法规标准-SAE J2802标准解读

SAE J2802是做什么的&#xff1f; SAE J2802全名为盲点监测系统&#xff08;BSMS&#xff09;&#xff1a;工作特性和用户界面&#xff0c;其中主要是对BSMS系统的工作特性及用户界面进行了介绍 BSMS目标 强制性目标 任何可获得公路许可的车辆&#xff0c;此处视为大于125 …

深入浅出二叉树— C语言版【数据结构】

目录 ​编辑 1.树概念及结构 1.1树的概念 1.2 树的相关概念 ​1.3 树的表示 2.二叉树概念及结构 2.1概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 简单二叉树题目练习 2.5 二叉树的存储结构 2.5.1 顺序存储——堆 2.5.2 链式存储 1.树概念及结构 1.1树的概念 树…

Java --- String类

&#xff08;一&#xff09;String java.lang.String 类代表字符串。Java 程序中所有的字符串文字&#xff08;例如 "hello" &#xff09;都可以看作是实现此类的实例。 字符串是常量&#xff0c;用双引号引起来表示。它们的值在创建之后不能更改。 字符串 St…