由于各种原因,被逼使用前台模板。看了一下其他模板库的实现,发现其原理并不难,遂决定重造轮子。使用ROR.erb风格。换言之,逻辑是写在"<%"与"%>"之间,如果是注释,则用"<%#"与"%>",凡是后台传过来的变量都加上@来标记。现在这个模板系统还没有搭载任何helper方法,也不依赖任何代码,因此非常精短。
<!doctype html>
<html>
<head>
<title>postMessage by 司徒正美</title>
<meta charset="utf-8"/>
<meta content="IE=8" http-equiv="X-UA-Compatible"/>
<meta name="keywords" content="t模板 by 司徒正美" />
<meta name="description" content="javascript模板 by 司徒正美" />
<title>javascript模板 by 司徒正美</title>
</head>
<body>
<h1>javascript模板 by 司徒正美</h1>
<textarea id="tmpl" style="display:none;" >
<h2><%= @name %></h2>
<%# 这是注释 %>
<ul>
<% for(var i=0; i<@supplies.length; i++) {%>
<li><%= @supplies[i] %></li>
<% } %>
</ul>
<p style="text-indent:2em;"><%=@address %></p>
</textarea>
<script>
(function () {
var metaObject = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'\\': '\\\\'
};
var dom = {
quote: function (str) {
str = str.replace(/[\x00-\x1f\\]/g, function (chr) {
var special = metaObject[chr];
return special ? special : '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4)
});
return '"' + str.replace(/"/g, '\\"') + '"';
}
};
//selector ,必须,String, 模板容器textarea的ID值
//json,必须,对象字面量或JSON
//onsite,可选,Boolean,是否就地替换掉模板容器,默认true,如果为false,则返回一个文档碎片,交由用户自己插入到需要的地方
dom.erb = function (selector, json, onsite) {
onsite = !!onsite|| true;
var el = document.getElementById(selector);
if (!el) throw "找不到目标元素";
var text = el.value,
erb = dom.erb;
if (!erb[selector]) {
var arr = text.split(/\s*<%\s*|\s*%>\s*/g),
buff = ["var __views = [];\n"]
for (var i = 0, segment, n = arr.length; i < n; i++) {
segment = arr[i]
if (i & 1) { //偶数项,处理动态的逻辑
switch (segment.charAt(0)) {
case "#":
//处理注释
buff.push(erb.startOfHTML, dom.quote("<!--" + segment + "-->"), erb.endOfHTML);
break;
case "=":
if (segment.indexOf("@") !== -1) { //处理后台返回的变量(输出到页面的)
buff.push(erb.startOfHTML, segment.slice(1).replace(erb.rAt, "$1json."), erb.endOfHTML);
} else {
buff.push(erb.startOfHTML, segment.slice(1), erb.endOfHTML)
}
break;
default:
if (segment.indexOf("@") !== -1) { //处理后台返回的变量
buff.push(segment.replace(erb.rAt, "$1json."), "\n");
} else {
buff.push(segment, "\n")
}
break;
}
} else { //奇数项,处理静态的HTML片断
buff.push(erb.startOfHTML, dom.quote(segment), erb.endOfHTML);
}
}
erb[selector] = new Function("json", buff.join("") + ';return __views.join("");')
}
var div = erb.parser;
div.innerHTML = erb[selector](json);
var fragment = document.createDocumentFragment();
while (div.firstChild) {
fragment.appendChild(div.firstChild)
}
return onsite ? el.parentNode.replaceChild(fragment, el) : fragment;
}
dom.erb.parser = document.createElement("div");
dom.erb.startOfHTML = "\t__views.push(";
dom.erb.endOfHTML = ");\n";
dom.erb.rAt = /(^|[^\w\u00c0-\uFFFF_])(@)(?=\w)/g;
window.dom = dom;
})();
window.onload = function(){
dom.erb("tmpl", {name:"司徒正美",
supplies:["第一个LI元素","第二个LI元素","第三个LI元素","第四个LI元素"],
address:"异次元"})
}
</script>
</body>
</html>