简体   繁体   English

jQuery查找与表达式匹配的所有先前元素

[英]jQuery to find all previous elements that match an expression

Using jQuery, how do you match elements that are prior to the current element in the DOM tree? 使用jQuery, 如何匹配DOM树中当前元素之前的元素? Using prevAll() only matches previous siblings. 使用prevAll()只匹配以前的兄弟姐妹。

eg: 例如:

<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>

In my specific case, I'll be searching for the first .findme element prior to the link clicked. 在我的具体情况下,我将在链接点击之前搜索第一个 .findme元素。

Ok, here's what I've come up with - hopefully it'll be useful in many different situations. 好的,这就是我想出来的 - 希望它在许多不同的情况下都有用。 It's 2 extensions to jQuery that I call prevALL and nextALL . 它是jQuery的2个扩展,我称之为prevALLnextALL While the standard prevAll() matches previous siblings, prevALL() matches ALL previous elements all the way up the DOM tree, similarly for nextAll() and nextALL() . 虽然标准的prevAll()与之前的兄弟prevALL()匹配, prevALL()匹配所有前面的元素,一直到DOM树,类似于nextAll()nextALL()

I'll try to explain it in the comments below: 我将在下面的评论中尝试解释它:

// 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;
    };
});

usage: 用法:

$('.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 - function rewritten from scratch. 编辑 - 从头开始​​重写的功能。 I just thought of a much quicker and simpler way to do it. 我只是想到了一种更快捷,更简单的方法。 Take a look at the edit history to see my first iteration. 查看编辑历史记录以查看我的第一次迭代。

edit again - found an easier way to do it! 再次编辑 - 找到了一种更简单的方法!

Presumably you are doing this inside an onclick handler so you have access to the element that was clicked. 大概你是在onclick处理程序中执行此操作,因此您可以访问被单击的元素。 What I would do is do a prevAll to see if it is at the same level. 我要做的是做一个prevAll来看它是否处于同一水平。 If not, then I would do a parent().prevAll() to get the previous siblings of the parent element, then iterate through those backwards, checking their contents for the desired element. 如果没有,那么我会做一个parent()。prevAll()来获取父元素的前一个兄弟,然后迭代这些兄弟元素,检查它们的内容是否有所需的元素。 Continue going up the DOM tree until you find what you want or hit the root of the DOM. 继续向上移动DOM树,直到找到你想要的或者命中DOM的根。 This a general algorithm. 这是一种通用算法。

If you know that it is inside a table, then you can simply get the row containing the element clicked and iterate backwards through the rows of the table from that row until you find one that contains the element desired. 如果您知道它在一个表中,那么您可以简单地获取包含该元素的行,并从该行向后遍历表的行,直到找到包含所需元素的行。

I don't think there is a way to do it in one (chained) statement. 我不认为有一种方法可以在一个(链式)声明中做到这一点。

edit : this solution works for both your original problem, the problem you mention in your first comment, and the problem you detail in the comment after that. 编辑 :此解决方案适用于您的原始问题,您在第一条评论中提到的问题,以及之后评论中详细说明的问题。

$('.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"
});

this works for your example in the OP as well as a modified example as explained in the comments: 这适用于OP中的示例以及注释中解释的修改示例:

<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>

as well as this test case which you added: 以及您添加的此测试用例:

<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>

I usually number elements (1,2,3..) (rel="number"), so then i use this code to give class to all previous elements: 我通常对元素(1,2,3 ..)(rel =“number”)进行编号,因此我使用此代码为所有以前的元素提供类:

var num = $(this).attr("rel");

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

had the same problem, heres what i came up with. 我遇到了同样的问题,我想出了什么。 my function uses compareDocumentPosition . 我的函数使用compareDocumentPosition dont know how it compares to the other solutions in terms of performance though. 虽然不知道它在性能方面与其他解决方案的比较。

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

of course one could change this quite easily to fetch ALL elements following the calling element. 当然,人们可以很容易地改变这一点来获取调用元素之后的所有元素。

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

EDIT: streamlined version 编辑:精简版

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM