广州有什么好玩的东西北京排名seo
目录
实验内容
目标分析
最终成果
实现过程
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>