用于节点操作的API,颠覆原生操作HTML DOM节点的

第一次看到敏捷开发的定义,我就被敏捷开发迷住了。通俗来说,敏捷开发可以让我们用过的代码可以再次重用,因为是再次重用,所以相对安全,再次调试也没有第一次那么费心,省时省力。不断重用代码的过程中把存在的bug不断的修复,也因为不断的去重用, 这个模板变得起越来越独立,适用的情况越来越广范,最后在安全方面达到铜墙铁壁,在开发方面达到随心所欲,在维护方面达到从容面对。

敏捷开发的确是利害,但如何练就这种深奥的武功呢?就我自身的情况靠人传授武功是不可能了,因为公司就我一个做开发的,苦思幂想之后,决定从开源的优秀框架入手,把它一行一行代码看懂,然后再为我所用。因为是一个人开发,前台和后台都得包办,哪从那一面做起呢? 之前有过一二个月的开发经验,觉得前台的JS很费时,而且老觉得在做重复的事情,比如发ajax请求,接收结果之后操作节点(有时候遇到不兼容的情况,如select和table在IE下不支持使用innerHTML,style在IE不会自动转化为字符串,要用cssText代替,一旦这些情况遇上,真得是无比打击程序员的积极性,因为你要为此花时间去找替代方案,去调试),还有节点轮换,弹出层,表单验证等一系列烦琐工作。所以我就坚决从前台的JS做起。为了练就怎么把JS的重用性提高,我选择向Jquery取经。花了几个月看了一大半,略有所得。我其中之一的JS模块“无限深度操作节点”(文采不好,名字不妥别见怪)出来了。有了它,我在操作节点方面变得容易,代码变得精简,而且不用写额外的代码去兼容浏览器,谈笑中,功能就完成了。

首先我谈谈是什么让操作节点给我们带来烦恼: 编写ajax程序时,动态增删改页面元素几乎是不可避免的,使用属性innerHTML就是我们经常利用的途径,但在IE中table,thead,tfoot,tbody,tr,col,colgroup,html,title,style,frameset的innerHTML属性是只读的,也就是我们不能使用innerHTML更新这些节点。(这里没有提到select,其实select也是不会成功的,估计是IE的bug)。例子如下:(下面的$id代表document.getElementById) <select id="jkit">
    <option value="1">jquery</option>
</select>
  //执行代码:
$id('jkit').innerHTML = '<option>jquery</option>';
//IE下并没有报任何错误,但select一个option节点都没有了。如果你对table使用innerHTML,IE会报unknown runtime error
     
对于这种情况比较常用的兼容方法是外加一个外层元素,例子如下: <div id="jkit">
    <select>
        <option value="1">jquery</option>
    </select>
</div>
  //执行代码:
$id('jkit').innerHTML = '<select><option value="1">jkit</option></select>';
//这样IE也成功改变select,但这种做法有个缺点,如果你对select注册过事件,这些事件会全部丢失,你要外加代码来重新注册事件。
     
在指定的节点前后新增节点,这就涉及于节点定位,节点创建以及给节点属性设置。使用innerHTML通常只用于覆盖DOM元素的所有节点,如果只想改变元素的某个子节点,或者只想在某个子节点前后增加节点,仍然使用innerHTML就适得其反,实现起来很吃力了,而且使用了innerHTML之后,对子节点注册过的事件肯定全部丢失掉。不使用innerHTML,那只好使用原生的DOM方法了,但这种代替方案也不好使,看下面例子: <select>
    <option value="1">jquery</option>
    <option value="2">jkit</option>
    <option value="3">mars</option>
</select>
  //现在我想在jkit之前多加一个option,用原生的DOM方法实现:
var newNode = document.createElement('option'),//新建一个节点
selector = document.getElementById('jkit3'),
/* 也可以用selector.options,但getElementsByTagName更通用。
那用childNodes怎么?最好也不要,对于空白节点,IE和FF的处理方式不一样,
就这例子,在FF中,select的firstChild是空白文本节点,
因为select和第一个option之间有换行以及空白字符,
FF会为其创建节点,而IE会忽略 */
options = document.getElementsByTagName('option');
newNode.setAttribute('value','new');
//newNode.setAttribute('text','NewNode');text不支持这样设置
//newNode.text = 'NewNode';ie不支持这种方式
newNode.innerHTML = 'NewNode';
selector.insertBefore(newNode,options[1]);//在kit之前插入
     
执行了上面的代码之后,select多了一个option:
<select>
    <option value="1">jquery</option>
    <option value="new">newNode</option>
    <option value="2">jkit</option>
    <option value="3">mars</option>
</select>
新增一个节点这要用到6句代码,现在只是新增一个节点,那对于批量操作怎么办啊?而且原生的DOM方法不怎么好用,名字长而且参数顺序不好记住,用的时候还得翻文档(除非你的开发的天天在操作节点,天天用这些方法)。有人说,也不怎么麻烦啊,我可以把这6句代码写成一个方法,然后通过传参来完成批量操作。某种程度上是可以减轻工作量,但看远一点,这也是不怎么现实的。原因有三:其一,每次新增的节点的属性不一样,加的位置不一样,你还得在方法外面确定新节点的位置以及在方法内部判断这个节点要加那些属些,下个节点要加哪个属性,把代码写在法方里面只能让你稍为轻松了些;其二,上面的代码可以看到,新增一个节点,你要排除很多不兼容的实现方法,由其在设置属性方面,每种元素有不一样的属性,在兼容方案上就有差异,你针对不同元素写这样的方法时,你必须要记住每种元素的兼容方案,可谁敢说能记住所有兼容方案,而且程序员想在技术上有所作为,靠的是思维逻辑的提升,而不是记忆,没必需在这方面下苦功;其三,一个项目是实现功能了,下一个项目再来,有相似的功能,但在细节上有差异,按上面的做法,可以想像的到你的代码重用性非常得低,因为要改变某些细节,你得重新理解曾经写下的逻辑,重新为你细节上的不同重构代码,这样一来,效率太低了,完全谈不上敏捷开发。

看到这样,估计读者都会问,如果不是这样做,难道开发真的可以做到复制粘贴吗?可以肯定的说,是不可能的。再全面的代码也不可能满足实现了不同包罗所有逻辑。但可以做得复用性很高,关键在于找出不同逻辑的共性并很好的独立出来。上面的操作节点做法我之所以说复用性低,是因为把操作节点跟特定的逻辑混合一起了,就是说特定逻辑的代码跟操作节点的代码关系太紧密了,到下一个逻辑又遇上节点操作时,再想重用上次的代码,就必须温习上次写下的逻辑,然后把操作节点的代码分离出来后再派上用场。下面介绍的由我开发的无限层次节点操作,就很好的从逻辑上独立出来,与逻辑无关。 上面的以select为例子,option是select的孩子节点,我们操作的是两层的树型结构,如果再深入一些,操作元素的子孙节点呢?如何定位元素后代节点呢?下面还是以原生的dom方法来实现: <table>
    <tr>
        <td>
            <ul>
                <li></li>
                <li></li>
            </ul>
        </td>
        <td>
            <ul>
                <li></li>
            </ul>
        </td>
    </tr>
</table>
  //现在我想在第一个li之前多加一个option,用原生的DOM方法实现:
newNode = document.createElement('li'),
table = document.getElementById('jkit4'),
//取li的父节点:
uls = table.getElementsByTagName('ul'),
/* getElementsByTagName虽然通用,但如果li标签里面嵌套了li,li父节点的兄弟节点也有li的话,那么getElementsByTagName都会取到这些节点,如果你的html结构真有哪么复杂,取出来结果后你也很难定位到你想找的li节点。遇到这情况,你只以通过childNodes一层层往下找,但之前提过childNodes在IE和FF中行为是不致的,所以你还要做兼容处理。*/
lis = table.getElementsByTagName('li');
newNode.innerHTML = 'NewNode';
//在指定位置插入
uls[0].insertBefore(newNode,lis[0]);
     
万变不离其中,跟上面的实现差不多,遇到复杂的html结构,可能就是定位比较麻烦,有人说定位不难,我可以在想要找的li上给定一个id属性,这样再怎么复杂也可以一步定位,但我想问如果要批量操作呢? 如果table有很多行,每一行第都涉及到这样的操作,那是不是每个li给定一人id ? 是可以,通过循环指也不怎么难,但我是反对这样的做法。如果有好的代替方案,我更赞成的是保持干净的HTML,在我开发的项目中,用于定位无素属性我是尽可能少给,因为我有足够灵活的方案寻节点。说到干净的HTML,这里说点题外话。怎么保持HTML的干净呢?在我开发中,HTML是不会混杂任何 逻辑性的javascript代码(只有接收后台数据的一些变量,而且这些变量统一放在HTML的最后面,变量太多的话,以组的形式组织),给元素注册事件也不会把onclick,onblur等事件代码嵌进html中,查看源码你不会在HTML中看到任何onclick,onblur等事件代码。回到这篇文章的主题吧!下面就这三点谈谈怎么让我们的工作变得简单!

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdfsgy.html