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 > <form action=""> <fieldset> <h2>Source</h2> <label for="source">Enter an HTML document fragment</label> <textarea cols="30" rows="15"> </textarea> <input type="button" value="↓ generate ↓" /> <h2>DOM Code</h2> <label for="result">and voila! DOM goodness:</label> <textarea 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 + '.;\n'; } } } break; } } window['generateDOM'] = generate; })();
- 上一篇 »JavaScript面向对象编程实战
- 下一篇 »JavaScript深入之参数按值传递