jQuery/Sizzle元素选择器上下文的一个陷阱的分析与解决

现有这样一段HTML:

<div id="div1">
    <span class="a">a1</span>
    <span class="b">b1</span>
    <span class="c">c1</span>
    <div id="div2">
        <span class="a">a2</span>
        <span class="b">b2</span>
        <span class="c">c2</span>
    </div>
</div>

现在用js去查找元素

jQuery

$('div .a, div .b, div .c',$('#div1'))

Sizzle

Sizzle('div .a, div .b, div .c',Sizzle('#div1'))

根据你的想象,上述js代码将返回哪些元素呢?

你可能想,要么就是所有的span,要么就是后面3个span,但其实是返回除了第一个span之外的所有span!

jQuery 1.4.3 到当前最新的 1.7.2 都存在该问题,但1.4.2没有该BUG.

分析了一下jQuery/Sizzle的源码,就明白了。

jquery-1.7.2.js 第5163行, 或者sizzle.js第1221行:

return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );

针对上面的例子,等价于

document.getElementById('div1').querySelectorAll("[id='div1'] div .a, div .b, div .c")
第一段是[id='div1'] div .a, 第二段是div .b, 第三段是div .c 所以导致第一个span被排除,第2第3个却未被排除。

弄清了原因,修正就很简单了,在query表达式每个逗号后面都加上[id=xxx] 的限定就行了。

好在是开源的,我已经在github向jquery/sizzle提交了pull request。

我把这句改成了如下:

return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query.replace(/\,/g,",[id='" + nid + "'] ") ), extra );
测试通过。

posted @ 2022-11-23 21:10  IginCui  阅读(16)  评论(0编辑  收藏  举报