具有兄弟姐妹方法的伪选择器

时间:2011-10-19 08:29:16

标签: jquery jquery-selectors

早些时候,我回答this question,这基本上是关于删除表格行。这个问题是关于该问题的评论的结果。给出以下HTML:

<div><a href="#" class="removelink">remove</a></div>
<table>
    <tr>
        <td>Row 1</td> 
    </tr>
</table>

以下jQuery:

$('.removelink').click(function(){
    $(this).parent().siblings('table tr:last').remove();
});

我希望没有任何事情发生,因为siblings方法应该选择当前匹配元素的兄弟,可选择由选择器过滤。来自jQuery文档:

  

该方法可选地接受相同类型的选择器表达式   我们可以传递给$()函数。如果提供选择器,则   元素将通过测试它们是否匹配来过滤。

基于此,我将上面的代码读作“获取当前元素(div)的兄弟姐妹,这是tr中的最后一个table”。显然,没有符合该描述的元素 - tr中有table,但它不是div的兄弟。所以,我不希望返回任何元素。但是,它实际上返回整个表,就像它完全忽略选择器的tr:last部分一样。

让我更加困惑的是,如果删除:last伪选择器,它会按预期工作(不返回任何元素)。

为什么上面的代码删除了整个表格?我只是愚蠢而且缺少明显的东西吗?您可以在行动here中看到上述代码。

修改 - 这是一个简化版本。给出以下HTML:

<div id="d1"></div>
<div>
    <span></span>
</div>

为什么以下jQuery返回第二个div

$("#d1").siblings("div span:last");

我希望它不返回任何内容,因为没有span#d1的兄弟。 Here's a fiddle这个简化的例子。

更新

在@muistooshort的精彩调查之后,我创建了一个jQuery bug ticket来跟踪这个问题。

2 个答案:

答案 0 :(得分:5)

请允许我稍微扩展我的评论。所有这些都基于您的第二个简化示例和jQuery 1.6.4。这可能有点冗长,但我们需要通过jQuery代码来了解它在做什么。


我们确实有jQuery源可用,所以让我们去徘徊 通过它,看看其中有什么奇迹。

siblings的胆量如下:

siblings: function( elem ) {
    return jQuery.sibling( elem.parentNode.firstChild, elem );
}

包含在此:

// `name` is "siblings", `fn` is the function above.
jQuery.fn[ name ] = function( until, selector ) {
    var ret = jQuery.map( this, fn, until )

    //...

    if ( selector && typeof selector === "string" ) {
        ret = jQuery.filter( selector, ret );
    }

    //...
};

然后jQuery.sibling就是这样:

sibling: function( n, elem ) {
    var r = [];

    for ( ; n; n = n.nextSibling ) {
        if ( n.nodeType === 1 && n !== elem ) {
            r.push( n );
        }
    }

    return r;
}

所以我们在DOM中迈出了一步,去了父母的第一个孩子, 并继续横向以获得所有父母的孩子(除了 我们开始的节点!)作为DOM元素的数组。

这使我们在retret = jQuery.filter( selector, ret ); 中拥有了所有兄弟DOM元素 现在来看看过滤:

filter

那么filter到底是什么? filter: function( expr, elems, not ) { //... return elems.length === 1 ? jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : jQuery.find.matches(expr, elems); } 就是这个:

elems

在您的情况下,#d1将只有一个元素(jQuery.find.matchesSelector 有一个兄弟姐妹)所以我们要去Sizzle.matchesSelector 实际上是var html = document.documentElement, matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; //... Sizzle.matchesSelector = function( node, expr ) { // Make sure that attribute selectors are quoted expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); if ( !Sizzle.isXML( node ) ) { try { if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { var ret = matches.call( node, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || !disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9, so check for that node.document && node.document.nodeType !== 11 ) { return ret; } } } catch(e) {} } return Sizzle(expr, null, null, [node]).length > 0; };

matchesSelector

一些实验表明Gecko和WebKit都没有 版本div span:first可以处理Sizzle(),因此我们结束了 在最后的matchesSelector电话中;请注意Gecko和WebKit div span变体可以处理div span和您的 jsfiddles在Sizzle(expr, null, null, [node])案例中按预期工作。

<span>做什么?为什么它返回一个数组 当然,在<div>内包含expr。我们会有 这在'div span:last'

node

,这在<div id="d2"> <span id="s1"></span> </div>

<span id="s1">

因此node内的expr与选择器很匹配 在Sizzle()中,<span>调用返回一个包含该数组的数组 matchesSelector由于该数组的长度不为零,console.log 呼叫返回true,一切都在废话中崩溃。

问题是在这种情况下jQuery没有正确地与Sizzle接口。恭喜你,你是一个蹦蹦跳跳的小虫子的骄傲之父。

这是一个(大规模)jsfiddle,带有内联版本的jQuery,有几个div span调用来支持我上面所说的内容:

  

http://jsfiddle.net/ambiguous/TxGXv/

有几点需要注意:

  1. 您将div span:nth-child(1)div span:first获得明智的结果;这两个都使用本机Gecko和WebKit选择器引擎。
  2. 您将获得与div span:lastdiv span:eq(0)甚至Sizzle()相同的已损坏的结果;所有这三个都通过Sizzle。
  3. 正在使用的{{1}}调用的四个参数版本未记录(请参阅Public API),因此我们不知道jQuery或Sizzle是否有问题。

答案 1 :(得分:0)

使用此更新

$('.removelink').click(function(){

$(this).parent().siblings('table').find('tr:last').remove();

});

检查Fiddle Example