【问题标题】:jQuery to find all previous elements that match an expressionjQuery查找与表达式匹配的所有先前元素
【发布时间】:2026-01-30 11:10:02
【问题描述】:

使用 jQuery,如何匹配 DOM 树中当前元素之前的元素? 使用 prevAll() 只匹配之前的兄弟元素。

例如:

<table>
    <tr>
        <td class="findme">find this one</td>
    </tr>
    <tr>
        <td><a href="#" class="myLinks">find the previous .findme</a></td>
    </tr>
    <tr>
        <td class="findme">don't find this one</td>
    </tr>
</table>

在我的具体情况下,我将在点击链接之前搜索 first .findme 元素。

【问题讨论】:

    标签: javascript jquery


    【解决方案1】:

    假设您是在 onclick 处理程序中执行此操作,因此您可以访问被单击的元素。我要做的是做一个 prevAll 看看它是否处于同一水平。如果没有,那么我会做一个 parent().prevAll() 来获取父元素的先前兄弟姐妹,然后向后遍历这些兄弟姐妹,检查它们的内容以获得所需的元素。继续向上 DOM 树,直到找到所需的内容或找到 DOM 的根。这是一个通用算法。

    如果您知道它在表格内,那么您可以简单地获取包含单击元素的行,然后从该行向后遍历表格的行,直到找到包含所需元素的行。

    我认为没有办法在一个(链式)语句中做到这一点。

    【讨论】:

    • 算法的好建议。我实际上实现了它(请参阅我的答案的第一个编辑),但运行速度有点慢。我最终做的是选择所有内容,然后在当前对象的任一侧对该数组进行切片,这似乎更快。
    【解决方案2】:

    编辑:此解决方案适用于您的原始问题、您在第一条评论中提到的问题以及您在之后的评论中详述的问题。

    $('.myLinks').click(function() {
        var findMe = '';
    
        $(this).parents().each(function() {
            var a = $(this).find('.findme').is('.findme');
            var b = $(this).find('.myLinks').is('.myLinks');
            if (a && b) {                             // look for first parent that
                                                      // contains .findme and .myLinks
                $(this).find('*').each(function() {
                    var name = $(this).attr('class');
                    if ( name == 'findme') {
                        findMe = $(this);             // set element to last matching
                                                      // .findme
                    }
                    if ( name == 'myLinks')    {
                        return false;                 // exit from the mess once we find
                                                      // .myLinks
                    }
                });
                return false;
            }   
        });
        alert(findMe.text() );               // alerts "find this one"
    });
    

    这适用于您在 OP 中的示例以及 cmets 中解释的修改示例:

    <table>
      <tr>
        <td class="findme">don't find this one</td>
      </tr>
      <tr>
        <td class="findme">find this one</td>
      </tr>
      <tr>
        <td><a href="#" class="myLinks">find the previous .findme</a></td>
      </tr>
      <tr>
        <td class="findme">don't find this one</td>
      </tr>
    </table>
    

    以及你添加的这个测试用例:

    <table>
      <tr>
        <td class="findme">don't find this one</td>
      </tr>
      <tr>
        <td class="findme">don't find this one</td>
      </tr>
      <tr>
        <td class="findme">find this one</td>
      </tr>
    </table>
    
    <a href="#" class="myLinks">find the previous .findme</a>
    

    【讨论】:

    • 嘿,这还不错!但是有几点:它不搜索当前节点的兄弟姐妹,并且由于您正在向前搜索而不是向后搜索(例如 parents()),我想您会想要使用 :last 而不是 :first 。我可能对这个问题有点模棱两可
    • 只要 .findme 在实际点击之前,它应该可以在任何标记中正常工作。这个特定的解决方案适用于您的测试代码。我可能误解了你,但它先搜索(通过父母)然后向前寻找 .findme
    • 或者你的意思是在链接之前有两个 .findme 的情况下,它应该匹配最后一个(在链接之前)?
    • 是的 :\ 对歧义感到抱歉
    • 努力点,但它不起作用对不起。如果你在表格后面放一个链接,它什么也找不到。我已经下定决心写了另一个解决方案,我现在正要写...
    【解决方案3】:

    好的,这就是我想出的 - 希望它在许多不同的情况下都会有用。这是我称之为prevALLnextALL 的jQuery 的两个扩展。标准的prevAll() 匹配之前的兄弟,prevALL() 匹配 DOM 树上所有之前的元素,nextAll()nextALL() 类似。

    我将尝试在下面的 cmets 中解释它:

    // this is a small helper extension i stole from
    // http://www.texotela.co.uk/code/jquery/reverse/
    // it merely reverses the order of a jQuery set.
    $.fn.reverse = function() {
        return this.pushStack(this.get().reverse(), arguments);
    };
    
    // create two new functions: prevALL and nextALL. they're very similar, hence this style.
    $.each( ['prev', 'next'], function(unusedIndex, name) {
        $.fn[ name + 'ALL' ] = function(matchExpr) {
            // get all the elements in the body, including the body.
            var $all = $('body').find('*').andSelf();
    
            // slice the $all object according to which way we're looking
            $all = (name == 'prev')
                 ? $all.slice(0, $all.index(this)).reverse()
                 : $all.slice($all.index(this) + 1)
            ;
            // filter the matches if specified
            if (matchExpr) $all = $all.filter(matchExpr);
            return $all;
        };
    });
    

    用法:

    $('.myLinks').click(function() {
        $(this)
            .prevALL('.findme:first')
            .html("You found me!")
        ;
    
        // set previous nodes to blue
        $(this).prevALL().css('backgroundColor', 'blue');
    
        // set following nodes to red
        $(this).nextALL().css('backgroundColor', 'red');
    });
    

    edit - 从头开始​​重写的函数。我只是想到了一种更快更简单的方法来做到这一点。查看编辑历史以了解我的第一次迭代。

    再次编辑 - 找到了更简单的方法!

    【讨论】:

    • 在“将以下节点设置为红色”调用中,这不应该是“nextALL()”吗?
    • 完美!完全按照我想要的方式顺利工作!不错。
    • 感谢这段代码,解决了我们一直在努力解决的问题! :D
    • 只是我的两分钱:我认为 prevOnWholeDocument / nextOnWholeDocument() 将是 much 这些函数更好的名称。它们更具描述性,写起来也不会很长,更不用说考虑到它们只会在这里和那里使用(我猜,)所以有点冗长不会有太大的伤害......
    • 这应该使用this.context而不是$( 'body' ),这样它也适用于其他文档中的元素。
    【解决方案4】:

    我通常给元素编号 (1,2,3..) (rel="number"),所以我使用这段代码为所有先前的元素赋予类:

    var num = $(this).attr("rel");
    
    for (var i = 1; i<=num; i++)
    {
        $('.class[rel="'+i+'"]').addClass("newclass");
    }
    

    【讨论】:

    • -1 用于回答 2.5 年前的问题。如果可以的话,我会给您另一个-1,以用明显的“不良做法”答案来回答。我会给你另一个 -1 以在该答案中包含损坏的代码。
    • 打倒自己 :) 2.5 岁的问题仍然是一个问题。
    • 我不同意你 -1 的第一个原因,如果有新的信息/意见,旧问题非常好回答。如果@John 想把他的小鸭子花在消极上,那么另外两个是可以投反对票的理由,但我个人认为,如此努力地否定新手有点残酷。尤其是当他很清楚他只是在分享他的方式时(他没有说这是最好的方式)。
    【解决方案5】:

    有同样的问题,这是我想出的。我的函数使用compareDocumentPosition。不知道它在性能方面与其他解决方案相比如何。

    $.fn.findNext = function ( selector ) {
      var found, self = this.get(0);
      $( selector )
        .each( function () {
          if ( self.compareDocumentPosition( this ) === 4 ){ 
            found = this; 
            return false;
          }    
        })
      return $(found);
    }
    

    当然可以很容易地改变这一点,以获取调用元素之后的所有元素。

    $.fn.nextALL= function ( selector ) {
      var found = [], self = this.get(0);
      $( selector )
        .each( function () {
          if ( self.compareDocumentPosition( this ) === 4 )
            found.push(this);          
        })
      return $(found);
    }
    

    编辑:精简版

    $.fn.findNext = function( s ){
        var m = this[0], f=function(n){return m.compareDocumentPosition(n)===4;};
        return this.pushStack( $(s).get().filter(f) );
    }
    

    【讨论】:

      最近更新 更多