JavaScript DOM高级程序设计 3.6 实例 将HTML代码转换成DOM代码(附源码)--我要坚持到底!
作为一名Web开发者,最讨厌的事情就是重复性任务,摆脱乏味的日常重复性事物的一种方法,是借助可重用的对象或者说与你现在建立的ADS库类似的库,另外一种让事情变得有意思,且能够加速开发进程的方式是编写能够创建代码的代码。
本节讲的工具,就是它可以在快速生成要的DOM代码是用来取代使用innerHTML字符串
HTML代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DOM Generation</title>
<title>AdvancED DOM Scripting Sample Document</title>
<!-- inclue some CSS style sheet to make everything look a little nicer -->
<link rel="stylesheet" type="text/css" href="../../shared/source.css" />
<link rel="stylesheet" type="text/css" href="../chapter.css" />
<link rel="stylesheet" type="text/css" href="style.css" />
<!-- Your ADS library with the common JavaScript objects -->
<script type="text/javascript" src="../../ADS-final-verbose.js"></script>
<!-- Log object from Chapter 2 -->
<script type="text/javascript" src="../../chapter2/myLogger-final/myLogger.js"></script>
<!-- The DOM generation file -->
<script type="text/javascript" src="generateDOM.js"></script>
<!-- The load script -->
<script type="text/javascript" src="load.js"></script>
</head>
<body>
<h1>DOM Generation</h1>
<div id="content">
<form id="generator" action="">
<fieldset>
<h2>Source</h2>
<label for="source">Enter an HTML document fragment</label>
<textarea id="source" cols="30" rows="15">
</textarea>
<input id="generate" type="button" value="↓ generate ↓" />
<h2>DOM Code</h2>
<label for="result">and voila! DOM goodness:</label>
<textarea id="result" cols="30" rows="15"></textarea>
</fieldset>
</form>
</div>
</body>
</html>
页面包含一个generateDOM.js文件和一个调用generateDOM()方法,转换HTML代码的非常简单的load.js脚本
//向页面中添加载入事件,注册事件侦听器
ADS.addEvent(window,'load',function(){
//在按钮上注册一个单机时间侦听器
ADS.addEvent('generate','click',function(W3CEvent){
//取得HTML源代码
var source=ADS.$('source').value;
//将HTML转换成DOM并放到#result文本区
ADS.$('result').value=generateDOM(source);
});
});
在构建generateDOM对象的框架之前,还需要想ADS.js添加几个方法
/*把对原型的修改放在ADS命名控件之外,是为了提醒你对内部String对象的prototype
的修改会影响到整个脚本中的每个字符串,而不仅仅是在ADS.generateDOM对象内部有影响
//重复一个字符串
if (!String.repeat)
{
String.prototype.repeat=function(s)
{
return new Array(s+1).join(this);
}
}
//var example='a'.repeat(5);
//example现在是aaaaa
//清除结尾 和开头处的空白符
if (!String.trim)
{
String.prototype.trim=function(){
retun this.replace(/^\s+|\s+$/g,'');
}
}
*/
在ADS.js库中添加一下代码
//把word-word转换为wordWord
//用于处理嵌入式样式的属性。
function camelize(s)
{
return s.replace(/-(\w/)/g,function(strMatch,p1){
return p1.toUpperCase();
});
}
window['ADS']['camelize']=camelize;
下面剩下唯一意见事就是在generateDOM.js文件中创建generateDOM对象了。框架以创建一个新的命名空间开始,人后包含了一些辅助方法和属性,最后是为window方法复制的代码:
//generateDOM对象的新命名空间
(function () {
//保证字符串是一个安全的js字符串,因为转换工具生成的字符串会包含在单引号中
//所以只需要转移反斜杠、单引号和换行符即可
function encode(str) {
if (!str) {
return null;
}
str = str.replace(/\\/g, '\\\\');
str = str.replace(/';/g, "\\'");
str = str.replace(/\s+^/mg, "\\n");
return str;
}
//查找所有节点中那些特殊的$var字符串。并对他们进行处理
//检查是否存在美元符号,如果是,则返回一个带引号的字符串或者一个变量名称
//而且还会把变量声明添加到requiredVariables字符串中。
function checkForVariable(v) {
if (v.indexOf('$') == -1) {
v = '\'' + v + '\'';
}
else {
//因MSIE会添加锚完整路径故需要取得该字符串从$到结尾出的子字符串
v = v.substring(v.indexOf('$') + 1);
requiredVariables += 'var' + v + ';\n'
}
return v;
}
var domCode = '';
var nodeNameCounters = [];
var requiredVariables = '';
var newVariables = '';
//借助如下代码,了解他内部工作过程
function generate(strHTML, strRoot) {
//将HTML代码添加到页面的主题中以便能遍历相应的DOM树
var domRoot = document.createElement('DIV');
//因为你可以控制在那个浏览器运行这个工具,所有innerHTML是可以使用的
domRoot.innerHTML = strHTML;
//重置变量
domCode = '';
nodeNameCounters = [];
requireVariables = '';
newVariables = '';
//使用processNode()处理domRoot中的所有子节点
var node = domRoot.firstChild;
while (node) {
ADS.walkTheDOMRecursive(processNode, node, 0, strRoot);
node = node.nextSibling;
}
//输出生成的代码
domCode =
'/*requiredVariables in this code\n' + requiredVariables + '*/\n\n'
+ domCode + '\n\n' + '/* new objects in this code\n' + newVariables + '*/\n\n';
return domCode;
}
//循环遍历子节点
function processNode(tabCount, refParent) {
//根据树的深度级别重复制表符以便对每一行进行适当的缩进,代码更清晰,更容易理解
var tabs = (tabCount ? '\t'.repeat(parseInt(tabCount)) : '');
//确定节点类型并处理元素和文本节点
switch (this.nodeType) {
//处理元素节点
case ADS.node.ELEMENT_NODE:
//计数器加1并创建一个使用标签和计数器的值表示的新变量,例如a1,a2,a3
if (nodeNameCounters[this.nodeName]) {
++nodeNameCounters[this.nodeName];
}
else {
nodeNameCounters[this.nodeName]=1
}
var ref = this.nodeName.toLowerCase() + nodeNameCounters[this.nodeName];
//添加创建这个元素的DOM代码航
domCode += tabs + 'var ' + ref + ' =document.createElement(\''
+ this.nodeName + '\');\n';
//将新变量添加到列表中以便在结束中报告他们
newVariables += '' + ref + ';\n';
//检测是否存在属性,如果是则循环遍历这些属性,并使用processAttribute()
//遍历他们的DOM树
if (this.attributes) {
for (var i = 0; i < this.attributes.length; i++) {
ADS.walkTheDOMRecursive(processAttribute, this.attributes[i], tabCount, ref);
}
}
break;
//处理文本节点
case ADS.node.TEXT_NODE:
//检测文本节点中除了空白符之外的值
var value = (this.nodeValue ? encode(this.nodeValue.trim()) : '');
if (value) {
//计数器加1并创建一个使用txt和计数器的值
//表示的新变量,例如txt1,txt2...
if (nodeNameCounters['txt']) {
++nodeNameCounters['txt'];
}
else {
nodeNameCounters['txt'] = 1;
}
var ref = 'txt' + nodeNameCounters['txt'];
//检查是不是$var格式的值
value = checkForVariable(value);
//添加创建这个元素的DOM代码
domCode += tabs + 'var' + ref + ' =document.createTextNode(' + value + ');\n';
//将新变量添加到列表中以便在结果中报告它们
newVariables += '' + ref + ';\n';
}
else {
//如果不存在值(或者只是空白符)则返回
//即这个节点将不会被添加到父节点中
return;
}
break;
default:
//忽略其他情况
break;
}
//添加将这个节点添加到父节点的代码
if (refParent) {
domCode += tabs + refParent + '.appendChild(' + ref + ');\n';
}
return ref;
}
function processAttribute(tabCount, refParent) {
//跳过文本节点
if (this.nodeType != ADS.node.ATTRIBUTE_NODE) {
return;
}
//取得属性值
var attrValue = (this.nodeValue ? encode(this.nodeValue.trim()) : '');
if (this.nodeName == 'cssText') {
alert('true');
}
//如果没有值返回
if (!attrValue) {
return;
}
//确定缩进级别
var tabs = (tabCount ? '\t'.repeat(parseInt(tabCount)) : '');
//根据nodeName进行判断,除了class和style需要特殊注意以外,所有类型都可以按常规来处理
switch (this.nodeName) {
default:
if (this.nodeName.substring(0, 2) == 'on') {
//如果属性名称以‘on’开头,说明是一个嵌入事件属性,
//也就需要重新创建一个给该属性复制的函数
domCode += tabs + refParent + '.' + this.nodeName + '=function(){' + attrValue + '}\n';
} else {
//对于其他情况则使用setAttribute
domCode += tabs + refParent + '.setAttribute(\'' + this.nodeName + '\'.' + checkForVariable(attrValue)
+ ');\n';
}
break;
case 'class':
//使用className属性为class赋值
domCode += tabs + refParent + '.className=' + checkForVariable(attrValue)
+ ';\n';
break;
case 'style':
//使用增则表达式基于;和临近的空格符来分割样式属性的值
var style = attrValue.split(/\s*;\s*/);
if (style) {
for (pair in style) {
if (!style[pair]) {
continue;
}
//使用增则表达式基于;和临近的空格符来分割样式属性的值
var prop = style[pair].split(/\s*:\s*/);
if (!prop[1]) {
continue;
}
//将css-property格式的css属性转换为cssProperty格式
prop[0] = ADS.camelize(prop[0]);
var propValue = checkForVariable(prop[1]);
if (prop[0] == 'float') {
//float是保留字,因此属特殊情况,cssFloat是标准属性
//styleFloat是ie使用的属性
domCode += tabs + refParent + '.style.cssFloat=' + propValue + ';\n';
domCode += tabs + refParent + '.style.styleFloat=' + propValue + ';\n';
}
else {
domCode += tabs + refParent + '.style=' + propValue + ';\n';
}
}
}
break;
}
}
window['generateDOM'] = generate;
})();