第4章
接 第2章 http://blog.youkuaiyun.com/zhgangxuan/article/details/50636985
第4章 下载地址 https://github.com/zhgangxuan/GXTemplate/blob/master/_oldfiles/version1/template04.html
源代码
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>bind attributes</title>
<script src="data.js"></script>
<style>
.templatedevelopmodetextnode {
color: firebrick;
text-decoration: underline;
}
</style>
</head>
<body>
<h1>{#myitem.name}</h1>
<p>
Address: {#myitem.address}
-
Zip: {#myitem.zipcode}
-
Phone: {#myitem.phone}
</p>
<p>
Link : <a target="_blank" bind.onbind="this.style.fontSize='2em';" bind-href="phoneinfo.html?phone={#myitem.phone}" οnclick="alert('Address is ' + myitem.address);return false;" οnmοuseοver="this.style.backgroundColor = 'yellow';" οnmοuseοut="this.style.backgroundColor = '';" style="display:inline-block;padding:5px 24px;">
{# myitem.name + " - (" + myitem.phone + ")" }
</a>
</p>
</body>
<script>
var re_template_textbinding = /{#([^}]+)}/g;
function ProcessEval($__exp__) {
return eval($__exp__);
}
function IsHtmlElement(item) {
if (window.HTMLElement)
return item instanceof HTMLElement;
return typeof (item) == "object" && !(item instanceof Object) && item.nodeType == 1; //IE7
}
function ProcessTextNode(node, str) {
var arr;
var pos = 0;
str.replace(re_template_textbinding, function (exp, g1, index, full) {
if (!arr) arr = [];
if (pos < index)
arr.push(str.substring(pos, index));
pos = index + exp.length;
var item = ProcessEval(g1);
if (item == null)
return;
if (IsHtmlElement(item)) {
arr.push(item);
return;
}
var span = document.createElement("span");
span.className = "templatedevelopmodetextnode";
span.title = exp;
span.innerText = item;
arr.push(span);
});
if (!arr)
return;
if (pos < str.length)
arr.push(str.substring(pos));
var p = node.parentNode;
for (var i = 0; i < arr.length; i++) {
var childnode = arr[i];
if (typeof (childnode) == "string") {
childnode = document.createTextNode(childnode);
}
p.insertBefore(childnode, node);
}
p.removeChild(node);
}
function TranslateAttribute(attr) {
var str = attr.nodeValue;
if (!str) return "";
return str.replace(re_template_textbinding, function (exp, g1, index, full) {
return ProcessEval(g1);
});
}
function TemplateExecute(node) {
if (node.nodeType == 3) {
var str = node.nodeValue;
if (str.indexOf('{#') != -1)
ProcessTextNode(node, str);
return;
}
if (node.nodeType != 1)
return;
switch (node.nodeName) {
case "SCRIPT":
case "STYLE":
return;
}
var attrs = node.attributes;
var bindarr;
var onbindscript;
for (var i = 0; i < attrs.length; i++) {
var attr = attrs.item(i);
if (!attr.specified)continue;//IE7
var attrname=attr.nodeName;
if (attrname == "bind.onbind") {
onbindscript = attr.nodeValue;
continue;
}
if (attrname.indexOf("bind-") != 0)
continue;
if (bindarr == null) bindarr = [];
bindarr.push(attr);
}
if (bindarr) {
for (var i = 0; i < bindarr.length; i++) {
var attr = bindarr[i];
var attrname = attr.nodeName.substring(5);
node.setAttribute(attrname, TranslateAttribute(attr));
}
}
var cns = node.childNodes;
for (var ni = cns.length; ni > 0; ni--)
TemplateExecute(cns.item(cns.length - ni));
if (onbindscript) {
var onbindfunc = new Function("", onbindscript);
onbindfunc.apply(node);
}
}
TemplateExecute(document.body);
</script>
</html>
目标:
在attribute上实现{#...}绑定语法
在写法上, 要求使用bind-src="...{#...}"这种方式.
实现过程:
不使用 src="...{#...}" 的原因是这种写法会导致页面加载过程中, 浏览器错误地处理未绑定的属性. 而且要判断所有attribute还会有一定的性能损失.
要操作attributes, 免不了要循环node.attributes集合:
var attrs = node.attributes;
var bindarr;
var onbindscript;
for (var i = 0; i < attrs.length; i++) {
var attr = attrs.item(i);
if (!attr.specified)continue;//IE7
var attrname=attr.nodeName;
if (attrname == "bind.onbind") {
onbindscript = attr.nodeValue;
continue;
}
if (attrname.indexOf("bind-") != 0)
continue;
if (bindarr == null) bindarr = [];
bindarr.push(attr);
}
if (bindarr) {
for (var i = 0; i < bindarr.length; i++) {
var attr = bindarr[i];
var attrname = attr.nodeName.substring(5);
node.setAttribute(attrname, TranslateAttribute(attr));
}
}
var cns = node.childNodes;
for (var ni = cns.length; ni > 0; ni--)
TemplateExecute(cns.item(cns.length - ni));
if (onbindscript) {
var onbindfunc = new Function("", onbindscript);
onbindfunc.apply(node);
}
第一次循环, 记录bind.onbind和所有以"bind-"开头的attribute
第二次循环, 把bindarr记录的每个bind-attribute经过TranslateAttribute处理后的值设置到attribute上去.
TranslateAttribute的原理和第1章一样, 只是简单的正规表达式加ProcessEval替换字符串.
最后, 在所有childNodes都处理完成后, 执行一次bind.onbind, 用apply方法, 把node当作this传递进去
例子HTML片段:
<p>
Link : <a target="_blank" bind.onbind="this.style.fontSize='2em';" bind-href="phoneinfo.html?phone={#myitem.phone}" οnclick="alert('Address is ' + myitem.address);return false;" οnmοuseοver="this.style.backgroundColor = 'yellow';" οnmοuseοut="this.style.backgroundColor = '';" style="display:inline-block;padding:5px 24px;">
{# myitem.name + " - (" + myitem.phone + ")" }
</a>
</p>
这里使用了bind.onbind, 其中this就是这个<a>自己. 这里可以调用外部函数, 去做更多的条件判断, 改写更多的显示样式等等. 也支持用this.parentNode.removeChild(this)来移除掉.
bind-href="phoneinfo.html?phone={#myitem.phone}" 最后会生成 href="phoneinfo.html?phone=0756-0000000"
实际上整个GXTemplate的v1.0版都已经上传到github的了. 接下来我们的业余时间主要是写写例子, 写写教程.
对于不懂得使用github的朋友, 可以直接下载源代码 https://github.com/zhgangxuan/GXTemplate/archive/master.zip