当前位置: 首页 > news >正文

组件化可编辑表格

目录

实验内容

目标分析

最终成果

实现过程

1.搭建HTML框架

2.自定义数据

3.创建EditableTable类,处理配置合并。

4.将表格组件挂载到指定的 DOM 元素并触发渲染

5.renderTable()生成表格DOM结构

创建表头

创建表体

6.表格配置

7.挂载表格

知识总结

constructor构造函数

mount()

Object.keys(this.config.cols).forEach(...)

check函数:对用户输入的数据进行实时校验,确保其符合业务规则。

完整代码


实验内容

完成以数据驱动模式动态生成页面中可交互的数据表格,并可根据任意结构化表格数据和配置信息进行可编辑表格的生成。

将该实验的代码合理组织为通用的可编辑表格Web组件生成代码。


目标分析

1.可编辑表格

2.由数据驱动的模块化表格


最终成果


实现过程

1.搭建HTML框架

<div class="table-box">
      <h2>成绩表</h2>
      <table id="scoreTable" class="table"></table>

      <h2>学生信息表</h2>
      <table id="infoTable" class="table"></table>
</div>

2.自定义数据


    "score":[
        {"name":"张三","chinese":100,"math":90,"english":80},
        {"name":"李四","chinese":90,"math":100,"english":80},
        {"name":"王五","chinese":80,"math":80,"english":100}
    ],
    "information":[
        {"name":"张三","sex":"男","age":18,"address":"北京"},
        {"name":"李四","sex":"男","age":19,"address":"上海"},
        {"name":"王五","sex":"女","age":17,"address":"广州"},
        {"name":"赵六","sex":"女","age":18,"address":"深圳"},
        {"name":"孙七","sex":"男","age":19,"address":"杭州"},
        {"name":"钱八","sex":"男","age":17,"address":"南京"}
    ]

3.创建EditableTable类,处理配置合并。

在初始化时保存传入的数据和配置对象,方便后续的方法使用。接下来,还有一个this.tableElement被初始化为null,会在其他方法中创建或引用一个表格DOM元素。

constructor(data, config) {
          this.data = data;
          this.config = config;
          this.tableElement = null;
        }

4.将表格组件挂载到指定的 DOM 元素并触发渲染

使用mount方法将组件挂载到指定地方。接收一个 DOM 元素element,将其赋值给this.tableElement。调用renderTable() 方法渲染表格内容到该元素中。

mount(element) {
          this.tableElement = element;
          this.renderTable();
        }

5.renderTable()生成表格DOM结构

创建表头

创建一个一个thead元素和一个tr元素作为表头的行。然后遍历this.confug.cols的键,对每一个键创建一个th元素,设置其内容为对应的列的标题。将th元素添加到表头的行中,形成表头。同时 ,如果配置中canDelete为true,则额外添加一个“操作”列。

Object.keys(this.config.cols)获取所有列的键,然后遍历每个键,创建th元素,并设置其内容为对应列的标题,最后将th添加到headerRow中。

          const thead = document.createElement("thead");
          const headerRow = document.createElement("tr");

          Object.keys(this.config.cols).forEach((key) => {
            const th = document.createElement("th");
            th.textContent = this.config.cols[key].title;
            headerRow.appendChild(th);
          });

          if (this.config.canDelete) {
            const operationTh = document.createElement("th");
            operationTh.textContent = "操作";
            headerRow.appendChild(operationTh);
          }

          thead.appendChild(headerRow);
          table.appendChild(thead);

创建表体

创建了一个tbody元素,遍历this.data数组中的每个项目,为每个项目创建一行(tr)。对于每个数据项,遍历配置中的列(this.config.cols),根据列配置创建单元格(td)。如果列配置为可编辑(editable),则创建input元素,并在输入框失去焦点时(blur事件)更新数据。如果配置允许删除(canDelete),则在每行末尾添加一个删除按钮,点击按钮会从数据中删除该行并重新渲染表格。

          const tbody = document.createElement("tbody");
          this.data.forEach((item, index) => {
            const row = document.createElement("tr");

            Object.keys(this.config.cols).forEach((key) => {
              const td = document.createElement("td");
              const configCol = this.config.cols[key];

              if (configCol.editable) {
                const input = document.createElement("input");
                input.value = item[key];
                input.addEventListener("blur", (e) => {
                  const val = e.target.value;
                  if (configCol.check) {
                    const isValid = configCol.check(val, e.target);
                    if (isValid) {
                      const newValue =
                        configCol.type === "number" ? parseFloat(val) : val;
                      item[key] = newValue;
                    }
                  } else {
                    item[key] = val;
                  }
                });
                td.appendChild(input);
              } else {
                td.textContent = item[key];
              }
              row.appendChild(td);
            });

            if (this.config.canDelete) {
              const deleteTd = document.createElement("td");
              const deleteButton = document.createElement("button");
              deleteButton.textContent = "删除";
              deleteButton.addEventListener("click", () => {
                this.data.splice(index, 1);
                this.renderTable();
              });
              deleteTd.appendChild(deleteButton);
              row.appendChild(deleteTd);
            }

            tbody.appendChild(row);
          });
          table.appendChild(tbody);

6.表格配置

通用属性:

  • tableCSS:表格CSS类名'table'。
  • canDelete:是否允许删除行
  • cols:列配置对象,键为列标识符。
配置项目成绩表信息表
列类型数值型混合型
校验规则数值范围(0-100)性别枚举、年龄整数、地址非空
错误反馈通过error类名高亮输入框,提示用户错误通过error类名高亮输入框,提示用户错误

check函数验证输入数据的有效性。

// 成绩表配置
      const scoreConfig = {
        tableCSS: "table",
        canDelete: true,
        cols: {
          name: { title: "姓名", editable: false },
          chinese: {
            title: "语文",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = !isNaN(val) && val >= 0 && val <= 100;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          math: {
            title: "数学",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = !isNaN(val) && val >= 0 && val <= 100;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          english: {
            title: "英语",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = !isNaN(val) && val >= 0 && val <= 100;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
        },
      };

      // 信息表配置
      const infoConfig = {
        tableCSS: "table",
        canDelete: true,
        cols: {
          name: { title: "姓名", editable: false },
          sex: {
            title: "性别",
            editable: true,
            check: (val, target) => {
              const isValid = ["男", "女"].includes(val);
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          age: {
            title: "年龄",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = Number.isInteger(Number(val)) && val > 0;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          address: {
            title: "地址",
            editable: true,
            check: (val, target) => {
              const isValid = val.trim().length > 0;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
        },
      };

7.挂载表格

创建了两个EditableTable实例,分别挂载到不同的DOM元素上。


      new EditableTable(scoreData, scoreConfig).mount(
        document.getElementById("scoreTable")
      );
      new EditableTable(informationData, infoConfig).mount(
        document.getElementById("infoTable")
      );

知识总结

constructor构造函数

constructor是一种用于创建和初始化class对象实例的特殊方法。一个类不能有一个以上的constructor方法。

参考网址:构造函数 - JavaScript | MDN

mount()

mount() 是类MyComponent的一个方法(本质是一个函数),需要通过实例调用。

const component = new MyComponent();
component.mount(document.getElementById("container"));

Object.keys(this.config.cols).forEach(...)

Object.keys(this.config.cols).forEach(...)是一种常见的遍历对象键的方式。使用Object.keys可以将对象的键转换为数组,然后使用数组的forEach方法进行遍历。

check函数:对用户输入的数据进行实时校验,确保其符合业务规则。

优点:

  • 每个列可自定义校验规则(如数字范围、枚举值、必填项)。
  • 校验规则与列配置绑定。
  • 即时反馈输入错误。

完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>信息表</title>
    <style>
      .table {
        width: 100%;
        border-collapse: collapse;
        margin: 20px 0;
      }

      .table th,
      .table td {
        border: 1px solid #ddd;
        padding: 8px;
        text-align: center;
      }

      .table th {
        background-color: #f2f2f2;
      }

      .error {
        background-color: crimson;
      }
      input {
        border: none;
      }
    </style>
  </head>

  <body>
    <div class="table-box">
      <h2>成绩表</h2>
      <table id="scoreTable" class="table"></table>

      <h2>学生信息表</h2>
      <table id="infoTable" class="table"></table>
    </div>
    <script>
      class EditableTable {
        constructor(data, config) {
          this.data = data;
          this.config = config;
          this.tableElement = null;
        }

        mount(element) {
          this.tableElement = element;
          this.renderTable();
        }

        renderTable() {
          const table = this.tableElement;
          table.innerHTML = "";

          // 创建表头
          const thead = document.createElement("thead");
          const headerRow = document.createElement("tr");

          Object.keys(this.config.cols).forEach((key) => {
            const th = document.createElement("th");
            th.textContent = this.config.cols[key].title;
            headerRow.appendChild(th);
          });

          if (this.config.canDelete) {
            const operationTh = document.createElement("th");
            operationTh.textContent = "操作";
            headerRow.appendChild(operationTh);
          }

          thead.appendChild(headerRow);
          table.appendChild(thead);

          // 创建表体
          const tbody = document.createElement("tbody");
          this.data.forEach((item, index) => {
            const row = document.createElement("tr");

            Object.keys(this.config.cols).forEach((key) => {
              const td = document.createElement("td");
              const configCol = this.config.cols[key];

              if (configCol.editable) {
                const input = document.createElement("input");
                input.value = item[key];
                input.addEventListener("blur", (e) => {
                  const val = e.target.value;
                  if (configCol.check) {
                    const isValid = configCol.check(val, e.target);
                    if (isValid) {
                      const newValue =
                        configCol.type === "number" ? parseFloat(val) : val;
                      item[key] = newValue;
                    }
                  } else {
                    item[key] = val;
                  }
                });
                td.appendChild(input);
              } else {
                td.textContent = item[key];
              }
              row.appendChild(td);
            });

            if (this.config.canDelete) {
              const deleteTd = document.createElement("td");
              const deleteButton = document.createElement("button");
              deleteButton.textContent = "删除";
              deleteButton.addEventListener("click", () => {
                this.data.splice(index, 1);
                this.renderTable();
              });
              deleteTd.appendChild(deleteButton);
              row.appendChild(deleteTd);
            }

            tbody.appendChild(row);
          });
          table.appendChild(tbody);
        }
      }

      // 成绩数据
      const scoreData = [
        { name: "张三", chinese: 100, math: 90, english: 80 },
        { name: "李四", chinese: 90, math: 100, english: 80 },
        { name: "王五", chinese: 80, math: 80, english: 100 },
      ];

      // 学生信息数据
      const informationData = [
        { name: "张三", sex: "男", age: 18, address: "北京" },
        { name: "李四", sex: "男", age: 19, address: "上海" },
        { name: "王五", sex: "女", age: 17, address: "广州" },
        { name: "赵六", sex: "女", age: 18, address: "深圳" },
        { name: "孙七", sex: "男", age: 19, address: "杭州" },
        { name: "钱八", sex: "男", age: 17, address: "南京" },
      ];

      // 成绩表配置
      const scoreConfig = {
        tableCSS: "table",
        canDelete: true,
        cols: {
          name: { title: "姓名", editable: false },
          chinese: {
            title: "语文",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = !isNaN(val) && val >= 0 && val <= 100;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          math: {
            title: "数学",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = !isNaN(val) && val >= 0 && val <= 100;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          english: {
            title: "英语",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = !isNaN(val) && val >= 0 && val <= 100;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
        },
      };

      // 信息表配置
      const infoConfig = {
        tableCSS: "table",
        canDelete: true,
        cols: {
          name: { title: "姓名", editable: false },
          sex: {
            title: "性别",
            editable: true,
            check: (val, target) => {
              const isValid = ["男", "女"].includes(val);
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          age: {
            title: "年龄",
            type: "number",
            editable: true,
            check: (val, target) => {
              const isValid = Number.isInteger(Number(val)) && val > 0;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
          address: {
            title: "地址",
            editable: true,
            check: (val, target) => {
              const isValid = val.trim().length > 0;
              target.classList.toggle("error", !isValid);
              return isValid;
            },
          },
        },
      };

      // 挂载表格
      new EditableTable(scoreData, scoreConfig).mount(
        document.getElementById("scoreTable")
      );
      new EditableTable(informationData, infoConfig).mount(
        document.getElementById("infoTable")
      );
    </script>
  </body>
</html>

相关文章:

  • Oracle for Linux安装和配置(11)——Linux命令
  • 《车辆人机工程-汽车驾驶操纵实验》
  • 嵌入式 C语言 二进制宏
  • SQL JOIN 全解析:跨表查询与实体关系建模
  • Qt实现读取本地文件并导出数据到Excel
  • 【连载2】基础智能体的进展与挑战综述
  • DeepSeek在消防救援领域的应用解决方案
  • 小程序开发指南
  • android display 笔记(十二)CPU,GPU,DPU的区别
  • Java中equals与 “==” 的区别
  • 自动驾驶的未来:多模态感知融合技术最新进展
  • HashMap实现通用的Request和Response及解析非标准JSON
  • 既然安装了WSL2和Ubuntu,那么怎么和windows传递文件(2)
  • 【Linux】Linux基础指令
  • vxe-table
  • FPGA上实现SD卡连续多块读的命令
  • muduo库源码分析: One Loop Per Thread
  • 解决windows server 2012服务器注册表删除Grace Period报错无法删除 GracePeriod: 删除项时出错
  • 数据结构day05
  • Centos7配置本地Yum源以及网络YUM源(保姆级)
  • 网站建设 齐鲁软件园/做一个网站需要多少钱大概
  • 网站推广运营/爱站网怎么使用
  • 个人博客网站制作搭建/百度指数什么意思
  • 建网站挣钱 优帮云/会计培训班哪个机构比较好
  • 达州建设局网站/网站推广优化方式
  • 制作一个简单网站的代码/网络热词的利弊