中级JavaScript例子, 如何实现一个简单实用的模板套用机制, GXTemplate , 第4章(估计要写9章)

原创 2016年02月05日 09:36:33

第4章

接 第2章 http://blog.csdn.net/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}" onclick="alert('Address is ' + myitem.address);return false;" onmouseover="this.style.backgroundColor = 'yellow';" onmouseout="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="...{#...}"这种方式.  

顺便实现一个bind.onbind事件. 在绑定过程中可执行自定义脚本

实现过程:

不使用 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}" onclick="alert('Address is ' + myitem.address);return false;" onmouseover="this.style.backgroundColor = 'yellow';" onmouseout="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



收藏助手
不良信息举报
您举报文章:中级JavaScript例子, 如何实现一个简单实用的模板套用机制, GXTemplate , 第4章(估计要写9章)
举报原因:
原因补充:

(最多只允许输入30个字)