平时工作中,相信很多小伙伴跟我一样,jquery的index()方法用的很多,也用得很爽,一直都觉得不可思议。今天就偶然想了解一下它的实现原理,便翻起了jquery的源码,窥得了它大致的实现原理。
场景重现:(jquery自行引入)
html:
<ul id="test">
<li id="t1">1</li>
<li id="t2">2</li>
<li id="t3">3</li>
</ul>
js:
$("#test").on('click', 'li', function(e) {
console.log($(this).index());
})
就如此小的一个demo,引发了我对index()方法的种种联想,为什么点击当前的元素可以知道它的索引呢?难道要预先知道它的父级吗,这不可能,这些dom节点又不是固定的,那到底。。。。。。
下面就开启源码之旅吧。
首先是index()方法,其源码如下:
index: function( elem ) {
// No argument, return index in parent
if ( !elem ) {
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
}
// Index in selector
if ( typeof elem === "string" ) {
return indexOf.call( jQuery( elem ), this[ 0 ] );
}
// Locate the position of the desired element
return indexOf.call( this,
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[ 0 ] : elem
);
}
源码很精简,而且我们只想看没传参时的index方法的用法,所以只看这句:
if ( !elem ) {
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
}
其实这句的重点在prevAll方法,其它都是些使逻辑严谨的代码,因此我们侧重点放在prevAll方法。所以,翻看了prevAll方法:
jQuery.each({
// ...此处省略了很多非相关代码
prevAll: function( elem ) {
return dir( elem, "previousSibling" );
},
}, function(name, fn) { // ...省略 })
紧接着,我们看dir源码:
function dir( elem, dir, until ) {
var matched = [],
truncate = until !== undefined;
while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
if ( elem.nodeType === 1 ) {
if ( truncate && jQuery( elem ).is( until ) ) {
break;
}
matched.push( elem );
}
}
return matched;
}
看到这里,其实大致思路已经知道了:遍历查找当前元素的上一个同级元素,形成一个包含所有该元素的前面的元素的集合。可能有点拗口,回到我们的小demo,在控制台模拟一下prevAll的过程,看着以下截图应该好理解些:

而dir函数已经为我们过滤了文本节点:
if ( elem.nodeType === 1 )
最终就是返回一个所有的前面元素的集合给我们了,然后index就是prevAll().length(该集合的长度)了。
ps: 可能细心的小伙伴会发现有漏洞,index源码上是这样的:
if ( !elem ) {
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
}
人家的prevAll明明没传参数,那按道理prevAll就接收不到elem参数,则elem为undefined,那就没有后续的故事情节了。关于这个,我只想说,你知道得太多了。。。。。。

由于本人精力有限,所以只能猜想是jQuery.each方法做了手脚,使得不传参的prevAll里面的elem自动变为this对象。(当然这只是本人猜想),希望有知道的留言一下,共勉,谢谢大家。
网友评论