jQuery, чтобы найти все предыдущие элементы, которые соответствуют выражению - PullRequest
10 голосов
/ 27 ноября 2008

Используя 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 до нажатия на ссылку.

Ответы [ 5 ]

20 голосов
/ 27 ноября 2008

Хорошо, вот что я придумала - надеюсь, это будет полезно во многих различных ситуациях. Это 2 расширения jQuery, которые я называю prevALL и nextALL. В то время как стандарт prevAll() соответствует предыдущим родным элементам, prevALL() соответствует ВСЕМ предыдущим элементам вплоть до дерева DOM, аналогично для nextAll() и nextALL().

Я постараюсь объяснить это в комментариях ниже:

// 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 - функция переписана с нуля. Я просто подумал о гораздо более быстром и простом способе сделать это. Взгляните на историю редактирования, чтобы увидеть мою первую итерацию.

отредактируйте снова - нашли более простой способ сделать это!

1 голос
/ 10 августа 2011

Я обычно нумерую элементы (1,2,3 ..) (rel = "number"), поэтому я использую этот код, чтобы дать класс всем предыдущим элементам:

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

for (var i = 1; i<=num; i++)
{
    $('.class[rel="'+i+'"]').addClass("newclass");
}
1 голос
/ 27 ноября 2008

edit : это решение подходит как для исходной проблемы, так и для проблемы, о которой вы упомянули в первом комментарии, и проблемы, которую вы подробно изложите в комментарии после этого.

$('.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, а также для измененного примера, как объяснено в комментариях:

<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>
1 голос
/ 27 ноября 2008

Предположительно, вы делаете это в обработчике onclick, поэтому у вас есть доступ к элементу, по которому щелкнули. То, что я хотел бы сделать, это сделать предварительный просмотр, чтобы увидеть, находится ли он на том же уровне. Если нет, то я бы сделал parent (). PrevAll (), чтобы получить предыдущих родственных элементов родительского элемента, а затем перебрать их в обратном порядке, проверяя их содержимое на предмет нужного элемента. Продолжайте идти вверх по дереву DOM, пока не найдете то, что хотите, или не достигните корня DOM. Это общий алгоритм.

Если вы знаете, что она находится внутри таблицы, вы можете просто получить строку, содержащую элемент, по которой щелкнули, и выполнить итерацию в обратном направлении по строкам таблицы из этой строки, пока не найдете ту, которая содержит требуемый элемент.

Я не думаю, что есть способ сделать это в одном (цепочечном) утверждении.

0 голосов
/ 03 декабря 2012

была такая же проблема, вот что я придумал. моя функция использует 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) );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...