实现前端框架数据绑定(数据驱动DOM)
一: 完整代码
function $(element){ //传入响应式区域的dom根节点,返回响应式数据 let $$ = {}; //响应式数据 function getElements(el){ //传入一个元素节点,返回一个保存着该元素本身及所有子元素的数组 var elementList = [], //存储所有的元素节点(最终结果) tempList = [], //暂时性的存储有子节点的元素节点 parentElement = []; //永远保存上一级的元素 parentElement.push(el); elementList.push(parentElement[0]); //把根元素推入list while(parentElement.length != 0){ for(let i = 0, len = parentElement.length; i < len; ++i){ let childElement = parentElement[i].childNodes; for(let j = 0, lens = childElement.length; j < lens; ++j){ if(childElement[j].nodeType == 1){ elementList.push(childElement[j]); if(childElement[j].hasChildNodes()){ tempList.push(childElement[j]); } } } } parentElement = tempList; tempList = []; } return elementList; } function attrHanding(elements){ //传入一个元素列表,返回bindData函数需要格式的数组 let list = []; for(let i = 0, len = elements.length; i < len; ++i){ let temp = elements[i].attributes; for(let j = 0, lens = temp.length; j < lens; ++j){ let temps = temp[j], testinga = temps.nodeName.indexOf(‘a:‘), testingb = temps.nodeName.indexOf(‘s:‘); if(testinga != -1 || testingb != -1){ let tem = temps.nodeName.split(‘&‘), end = ‘‘, tems = (tem[0].split(‘:‘))[1]; tems = tems.split(‘‘); for(let x = 0, l = tems.length; x < l; ++x){ if(tems[x] != ‘-‘){ end = end + tems[x]; }else{ ++x; end = end + tems[x].toUpperCase(); } } list.push(tem[1], elements[i]); if(testinga != -1){ list.push(end, ‘‘, temp[j].nodeValue); }else{ list.push(‘style‘, end, temp[j].nodeValue); } } } } return list; } function bindData(s){ //需要给bind传入数组,5个一组:(dom替代的名字,要操作的dom元素,要操作的属性名1,要操作的属性名2,dom替代的名字初始值) for(let i = 0, l = s.length; i < l; i += 5){ //把$$里的数据和dom绑定到一起,$$发生变动时,自动修改dom Object.defineProperty($$, s[i], { get: function(){ return this[‘_‘ + s[i]]; }, set: function(v){ this[‘_‘ + s[i]] = v; if(s[i + 3] == ""){ s[i + 1][s[i + 2]] = v; }else{ s[i + 1][s[i + 2]][s[i + 3]] = v; } } }); $$[s[i]] = s[i + 4]; //初始值 } } bindData(attrHanding(getElements(element))); return $$; }
二:语法说明
1:对于一般的html元素属性,可以这样绑定 a:属性名&绑定的变量名=‘初始值‘
比如: a:title&tit="Hello DataBinding"(将title属性和变量tit绑定在一起)
2:对于行内样式,需要这样绑定 s:属性名&绑定的变量名=‘初始值‘
比如: s:color&color="red"(将行内样式color属性和color变量绑定在一起)
3:引入完整代码后,把你需要控制的区域作为一个元素传入$()函数,它会返回一个对象,你需要接收它,里面包含了所有响应式的元素属性
比如:我定义了data变量来接受,那么我就可以用data.变量名=" "这样的方式来操纵它
三:需要注意的点
因为html不区分大小写,所以变量名需要小写,对于包含大写字母的属性,比如innerHTML,需要这样写:inner-h-t-m-l
四:案例演示
新建一个文件夹,进入,新建一个databinding.js文件,把完整代码粘贴进去,保存,再新建一个html文件,粘贴以下代码,保存,然后双击打开,在浏览器里应该可以看到效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据绑定</title>
</head>
<body>
<div id="root">
<p a:title&tit="Hello" s:font-weight&weight="300">请把鼠标移动到我身上</p>
<button onclick="coarsen()">点击我让字体变粗</button>
</div>
<script src="./databinding.js"></script>
<script>
var data = $(document.getElementById(‘root‘));
data.tit = "如果看到我,说明你绑定成功";
function coarsen(){
data.weight = "700";
}
</script>
</body>
</html>
五:实现思路及代码组织
遍历需要控制区域的所有元素(封装函数getElements),
遍历每个元素的所有属性,找到符合语法的属性进行解析(封装函数attrHanding),
利用Object.defineProperty()函数重定义get和set(封装函数bindData),
然后定义函数$,在$函数内定义一个空对象{}用于存放响应式属性变量,
再把前面的三个函数放到$函数内,依次嵌套调用bindData attrHanding getElements,返回定义的空对象