引言
jQuery支持丰富的DOM节点操作:
- 属性设置
- attributes.js
- css.js
- data.js
- 插入或删除节点
- manipulation.js
- 节点遍历
- traversing.js
由于JavaScript已经对操作DOM节点提供了基础性支持,jQuery在实现这些功能的时候,主要是提供一层封装。仔细研读源码实现,仍可收获许多启发:
attributes.js
关注其核心方法attr,根据jQuery API 文档, attr方法主要有两种调用形式:
.attr( attributeName ):获取首个匹配节点的属性值.attr( attributeName, value ): 设置所有匹配节点的属性值, 有三种调用形式:.attr( attributeName, value ):value是普通的String类型.attr( attributes ):attributes是一个由attribute-value键值对构成的PlainObject.attr( attributeName, function ): 需设置的属性值由function的返回值提供
片段一
实际的代码实现:
1 | jQuery.fn.extend( { |
access方法:
1 | // Multifunctional method to get and set values of a collection |
这里需要指出的是,之前提到的attr方法的各种调用形式,无论是get还是set, 无论是传递plain object还是function,所有的实现都是在access中,导致access的内部代码比较凌乱复杂。
这是一个值得商榷的设计,因为它违反了KISS原则,为了获得灵活性牺牲了可读性。当然,由于JavaScript本身传递函数参数的缺陷(例如不支持函数重载), 这么做也是无奈之举。
css.js和data.js
和attr.js类似,css.js的目的是为了存取节点的css属性,data.js的目的是为了存取节点私有数据。这三个文件的实现代码存在很大程度的重复,是jQuery未来实现可以改进的地方。
manipulation.js
manipulation.js中实现了操作DOM节点(比如插入和删除)的逻辑。
片段二
拿插入方法append举例:
1 | append: function() { |
可以看到,底层调用了javascript的appendChild方法,来实现插入节点,比较简单。
片段三
这一段是manipulation.js中最有趣的代码:
1 | jQuery.each( { |
对于append方法,有一个对应的类似方法appendTo, 二者的唯一区别在于调换了source和target,相应的还有prependTo, insertBefore, insertAfter和replaceAll, 他们都是基于原方法,调用jQuery( insert[ i ] )[ original ]( elems );得以交换source和target。
这类优雅的实现,得益于JavaScript语言本身较强的动态性,极大地减少了重复代码。
traversing.js
traversing.js中实现了遍历DOM节点树的逻辑。由于JavaScript本身已经对遍历DOM节点树提供了较好的支持,jQuery只是提供了一层很浅的封装。
片段四
抽取一段有趣的代码:
1 |
|
许多遍历方法如parents,nextAll, prevUntil,底层都是调用了dir方法实现,dir方法是如何定义的的呢?
片段五
1 | return function( elem, dir, until ) { |
根据传入的dir及until参数,按方向遍历获取所有满足条件的节点,再次展现了JavaScript较强的动态性。