【用JS自制表格软件玩数据】3. 从零构建实时渲染器

当写完本系列后,我会把源代码分享出来给大家。本课程也会持续更新与矫正。欢迎留言指正!

功能简介

前面第一节内容的时候,提到的模板,使用的是HTML标签,现在需要开始做一个核心功能就是把读取到的excel 文件里的内容渲染到页面中来。

在渲染方面,我们一般已知的就有比较知名的 Angular, React, Vue 等等框架,但是由于这里将会有很多定制化的功能要做,加载第三方框架的话,就需要安装大量的第三方辅助包,会使得整个项目更加复杂和臃肿。
还有一个更关键的原因是,第三方框架的版本更新迭代,会影响到项目的复杂度。综上考虑,促使我要自己实现这个渲染器

工作流的结构

数据流
事件流
渲染数据
分析器
创建DOM
绑定事件
最终渲染展示

HTML 标签元素特点分析

一个普通的 HTML 标签元素长什么样?

<div id="iamid" title="title" style="color:#ff0000;" data-dataKey="dataValue">
	我是文本内容
</div>

所以,我们要先定义一个渲染器接收的数据流结构,我给它起名为:渲染树。渲染树的数据结构应该是长什么样的呢?请看下面:

[
	{
		"Tag" : "div",
		"ID" : "iamid",
		"title" : "title",
		"ClassName" : "",
		"Style" : "",
		"Data" : [
			{
				"k":"dataKey",
				"v":"dataValue"
			}
		],
		"Text":"我是文本内容",
		"Action":{
			"click":function(){console.log("click Action")},
			"change":function(){console.log("change Action")},
		},
		"Child":[
			{
				"Tag" : "span",
				"ClassName" : "",
				"Style" : "",
				"Data" : [
					{
						"k":"dataKey",
						"v":"dataValue"
					}
				],
				"Text":"我是文本内容",
				"Action":{
					"click":function(){console.log("click Action")},
					"change":function(){console.log("change Action")},
				},
				"Child":[]
			}
		]
	}
]

属性分析:

属性功能描述
Tag标签(div, span, li, ul 等等)
Datadata-* 属性用于存储页面或应用程序的私有自定义数据。
ID标签的ID
ClassNameclass 样式类的名称
Style样式
Action事件绑定
Child子标签,这是一个数组

渲染器的源代码

创建一个渲染器类

class renderDOM{
	/**
	* 初始化渲染器
	*/
	constructor() {
	
	}

	/**
	* 创建一个渲染方法
	*/
	renderDOM(){
		
	}
}

我把渲染器函数命名为:renderDOM

会传入四个参数:

  1. mainid(父级节点的ID)
  2. DomTree(整个渲染树)
  3. begin(渲染树的事件发生前,会执行 begin 函数)
  4. end(渲染树的事件发生后,会执行 end 函数)
renderDOM(mainid,DomTree,begin,end){
		var begin = begin || function(){console.log("begin")};
		var end = end || function(){console.log("end")};
		
		var that = this;
		var oMain = document.getElementById(mainid);
		var fragment = document.createDocumentFragment();

		var Celement;
		var ChildList = [];
		for(var i in DomTree){
			if(typeof DomTree[i]["Tag"] === 'string'){
				Celement = document.createElement(DomTree[i]["Tag"]);
				if(typeof DomTree[i]["ID"] === 'string'){
					Celement.setAttribute("id",DomTree[i]["ID"]);
				}
				if(typeof DomTree[i]["Title"] === 'string'){
					Celement.setAttribute("title",DomTree[i]["Title"]);
				}
				if(typeof DomTree[i]["ClassName"] === 'string'){
					Celement.setAttribute("class",DomTree[i]["ClassName"]);
				}
				if(typeof DomTree[i]["Style"] === 'string'){
					Celement.setAttribute("style",DomTree[i]["Style"]);
				}
				if(typeof DomTree[i]["selected"] === 'string'){
					Celement.setAttribute("selected",DomTree[i]["selected"]);
				}
				if(typeof DomTree[i]["Data"] === 'object'){
					for(var j in DomTree[i]["Data"]){
						Celement.setAttribute("data-"+DomTree[i]["Data"][j]["k"],DomTree[i]["Data"][j]["v"]);
					}
				}
				if(typeof DomTree[i]["Contenteditable"] === 'string'){
					// 该处可以设置 div 元素为可编辑区域
					Celement.contenteditable = DomTree[i]["Contenteditable"];
				}
				
				if(typeof DomTree[i]["Text"] === 'string'){
					Celement.innerHTML = DomTree[i]["Text"];
				}
				if(typeof DomTree[i]["Child"] === 'object'){
					if(DomTree[i]["Child"].length>0){
						ChildList.push({
							"mainid" : DomTree[i]["ID"],
							"DomTree" : DomTree[i]["Child"]
						});
					}
				}
				fragment.appendChild(Celement);
			}
		}
		oMain.appendChild(fragment);

		// 渲染子节点
		if(ChildList.length>0){
			for(var i in ChildList){
				this.renderDOM(ChildList[i]["mainid"],ChildList[i]["DomTree"],begin,end);
			}
		}
	}

渲染器新增了事件绑定

现在开始在渲染模块里面,新增一个事件方法,所有的事件创建之后都会存放在一个大集合里面。方便随时可以调用。

clickevent 就是用于存放所有事件的 map 对象集合。

在渲染器里,新增了对事件代码的绑定。

if(typeof DomTree[i]["Action"] === 'object'){
	if(typeof DomTree[i]["Hotkey"] === 'string'){ // 设置热键
		that.addKeyEvent(DomTree[i]["Hotkey"],(typeof DomTree[i]["Action"]["click"] === 'function')?DomTree[i]["Action"]["click"]:function(){});
	}
	if(typeof DomTree[i]["Action"]["text"] === 'function'){ // 设置多语言界面
		that.UIevent[DomTree[i]["ID"]] = DomTree[i]["Action"]["text"];
		Celement.innerHTML = DomTree[i]["Action"]["text"](this.UITextSwitch);
	}
	that.clickevent[DomTree[i]["ID"]] = {
		"begin":begin,
		"readystatechange":(typeof DomTree[i]["Action"]["readystatechange"] === 'function')?DomTree[i]["Action"]["readystatechange"]:function(){},
		"click":(typeof DomTree[i]["Action"]["click"] === 'function')?DomTree[i]["Action"]["click"]:function(){},
		"dblclick":(typeof DomTree[i]["Action"]["dblclick"] === 'function')?DomTree[i]["Action"]["dblclick"]:function(){},
		"mousedown":(typeof DomTree[i]["Action"]["mousedown"] === 'function')?DomTree[i]["Action"]["mousedown"]:function(){},
		"mouseup":(typeof DomTree[i]["Action"]["mouseup"] === 'function')?DomTree[i]["Action"]["mouseup"]:function(){},
		"change":(typeof DomTree[i]["Action"]["change"] === 'function')?DomTree[i]["Action"]["change"]:function(){},
		"mousemove":(typeof DomTree[i]["Action"]["mousemove"] === 'function')?DomTree[i]["Action"]["mousemove"]:function(){},
		"mouseover":(typeof DomTree[i]["Action"]["mouseover"] === 'function')?DomTree[i]["Action"]["mouseover"]:function(){},
		"mouseout":(typeof DomTree[i]["Action"]["mouseout"] === 'function')?DomTree[i]["Action"]["mouseout"]:function(){},
		"contextmenu":(typeof DomTree[i]["Action"]["contextmenu"] === 'function')?DomTree[i]["Action"]["contextmenu"]:function(){},
		"end":end
	};
	if(typeof DomTree[i]["Action"]["mousewheel"] === 'function'){
		Celement.addEventListener('mousewheel', DomTree[i]["Action"]["mousewheel"], false)
	}
	if(typeof DomTree[i]["Action"]["mousemove"] === 'function'){
		Celement.addEventListener('mousemove', DomTree[i]["Action"]["mousemove"], false)
	}
}

所以整个代码结构变成这样:


	renderDOM(mainid,DomTree,begin,end){
		var begin = begin || function(){console.log("begin")};
		var end = end || function(){console.log("end")};
		
		var that = this;
		var oMain = document.getElementById(mainid);
		var fragment = document.createDocumentFragment();

		var Celement;
		var ChildList = [];
		for(var i in DomTree){
			if(typeof DomTree[i]["Tag"] === 'string'){
				Celement = document.createElement(DomTree[i]["Tag"]);
				if(typeof DomTree[i]["ID"] === 'string'){
					Celement.setAttribute("id",DomTree[i]["ID"]);
				}
				if(typeof DomTree[i]["Title"] === 'string'){
					Celement.setAttribute("title",DomTree[i]["Title"]);
				}
				if(typeof DomTree[i]["ClassName"] === 'string'){
					Celement.setAttribute("class",DomTree[i]["ClassName"]);
				}
				if(typeof DomTree[i]["Style"] === 'string'){
					Celement.setAttribute("style",DomTree[i]["Style"]);
				}
				if(typeof DomTree[i]["selected"] === 'string'){
					Celement.setAttribute("selected",DomTree[i]["selected"]);
				}
				if(typeof DomTree[i]["Data"] === 'object'){
					for(var j in DomTree[i]["Data"]){
						Celement.setAttribute("data-"+DomTree[i]["Data"][j]["k"],DomTree[i]["Data"][j]["v"]);
					}
				}
				if(typeof DomTree[i]["Contenteditable"] === 'string'){
					// 该处可以设置 div 元素为可编辑区域
					Celement.contenteditable = DomTree[i]["Contenteditable"];
				}
				if(typeof DomTree[i]["Action"] === 'object'){
					if(typeof DomTree[i]["Hotkey"] === 'string'){ // 设置热键
						that.addKeyEvent(DomTree[i]["Hotkey"],(typeof DomTree[i]["Action"]["click"] === 'function')?DomTree[i]["Action"]["click"]:function(){});
					}
					if(typeof DomTree[i]["Action"]["text"] === 'function'){ // 设置多语言界面
						that.UIevent[DomTree[i]["ID"]] = DomTree[i]["Action"]["text"];
						Celement.innerHTML = DomTree[i]["Action"]["text"](this.UITextSwitch);
					}
					that.clickevent[DomTree[i]["ID"]] = {
						"begin":begin,
						"readystatechange":(typeof DomTree[i]["Action"]["readystatechange"] === 'function')?DomTree[i]["Action"]["readystatechange"]:function(){},
						"click":(typeof DomTree[i]["Action"]["click"] === 'function')?DomTree[i]["Action"]["click"]:function(){},
						"dblclick":(typeof DomTree[i]["Action"]["dblclick"] === 'function')?DomTree[i]["Action"]["dblclick"]:function(){},
						"mousedown":(typeof DomTree[i]["Action"]["mousedown"] === 'function')?DomTree[i]["Action"]["mousedown"]:function(){},
						"mouseup":(typeof DomTree[i]["Action"]["mouseup"] === 'function')?DomTree[i]["Action"]["mouseup"]:function(){},
						"change":(typeof DomTree[i]["Action"]["change"] === 'function')?DomTree[i]["Action"]["change"]:function(){},
						"mousemove":(typeof DomTree[i]["Action"]["mousemove"] === 'function')?DomTree[i]["Action"]["mousemove"]:function(){},
						"mouseover":(typeof DomTree[i]["Action"]["mouseover"] === 'function')?DomTree[i]["Action"]["mouseover"]:function(){},
						"mouseout":(typeof DomTree[i]["Action"]["mouseout"] === 'function')?DomTree[i]["Action"]["mouseout"]:function(){},
						"contextmenu":(typeof DomTree[i]["Action"]["contextmenu"] === 'function')?DomTree[i]["Action"]["contextmenu"]:function(){},
						"end":end
					};
					if(typeof DomTree[i]["Action"]["mousewheel"] === 'function'){
						Celement.addEventListener('mousewheel', DomTree[i]["Action"]["mousewheel"], false)
					}
					if(typeof DomTree[i]["Action"]["mousemove"] === 'function'){
						Celement.addEventListener('mousemove', DomTree[i]["Action"]["mousemove"], false)
					}
				}
				if(typeof DomTree[i]["Text"] === 'string'){
					Celement.innerHTML = DomTree[i]["Text"];
				}
				if(typeof DomTree[i]["Child"] === 'object'){
					if(DomTree[i]["Child"].length>0){
						ChildList.push({
							"mainid" : DomTree[i]["ID"],
							"DomTree" : DomTree[i]["Child"]
						});
					}
				}
				fragment.appendChild(Celement);
			}
		}
		oMain.appendChild(fragment);

		// 渲染子节点
		if(ChildList.length>0){
			for(var i in ChildList){
				this.renderDOM(ChildList[i]["mainid"],ChildList[i]["DomTree"],begin,end);
			}
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妇男主任

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值