JQuery .each () в обратном направлении

442

Я использую JQuery, чтобы выбрать некоторые элементы на странице, а затем переместить их в DOM. У меня проблема в том, что мне нужно выбрать все элементы в обратном порядке, который JQuery, естественно, хочет выбрать. Например:

<ul>
   <li>Item 1</li>
   <li>Item 2</li>
   <li>Item 3</li>
   <li>Item 4</li>
   <li>Item 5</li>
</ul>

Я хочу выбрать все элементы li и использовать для них команду .each (), но я хочу начать с пункта 5, затем пункта 4 и т. Д. Возможно ли это?

Джек Миллс
источник

Ответы:

681
$($("li").get().reverse()).each(function() { /* ... */ });
Джо Чунг
источник
10
Важно отметить, что индекс не перевернут, поэтому, если вы хотите, например, только последние три, вы не можете ожидать, что индекс будет равен <li>Item 5</li>0.
pathfinder
8
Дэвиду Андресу: может быть, вы забыли добавить дополнительные $ () перед li. Я сделал эту ошибку и получил "неопределенный функционал".
Михал - wereda-net
2
@DavidAndres: Вы пропустили то, .get()что превращает упакованный массив jQuery в обычный массив JS. Массив JS имеет .reverse().
stackular
1
Внимание: Array.reverse()оба изменяют массив на месте и возвращают обратный массив. Таким образом, это решение на самом деле основывается на том, что $ (). Get () возвращает новый массив, а не ссылку на какой-либо внутренний массив jQuery или DOM. Вероятно, безопасная ставка в рамках этого примера. Будьте кодировщиком.
Боб Стейн
404

Я представляю вас самым чистым способом из самых маленьких в мире плагинов jquery:

jQuery.fn.reverse = [].reverse;

Применение:

$('jquery-selectors-go-here').reverse().each(function () {
    //business as usual goes here
});

-Все благодарность Майклу Гири в его посте здесь: http://www.mail-archive.com/discuss@jquery.com/msg04261.html

Уолдо Хэмптон
источник
Хорошо, я согласен, что это круто, но почему это работает? Вы копируете (свободно говоря) Array.prototype.reverse в jQuery.prototype.reverse, и когда он вызывается, thisобъект имеет числовые свойства и свойство длины, поэтому javascript может индексировать его и может применять функцию массива к экземпляру jQuery?
mlhDev
5
Важно отметить, что индекс не перевернут, поэтому, если вы хотите, например, только последние три, вы не можете ожидать, что индекс будет равен <li>Item 5</li>0.
pathfinder
1
@ Матфея - Да, вы все правильно поняли. Этот код действительно скопировать ссылку на Array.prototype.reverseк jQuery.prototype.reverse. Многие из методов массива JavaScript будут отлично работать на любом объекте, который достаточно похож на массив - то есть, если объект имеет .lengthсвойства числового индекса. reverseне зависит от некоторого внутреннего представления массива; он получает доступ к массиву , используя нормальный .length, [0], и [1]т.д. так же , как код массива вы пишете сами.
Майкл Гири
8
Не расточительно ли создавать экземпляр нового массива только для того, чтобы получить его reverseметод? Разве не было бы быстрее просто скопировать ссылку на функцию Array.prototype.reverse, например jQuery.fn.reverse = Array.prototype.reverse;? Не такой короткий и «умный», но более эффективный? По общему признанию это, вероятно, будет незаметно в современных молниеносных браузерах, но все же ...
zachelrath
3
@zachelrath Да, я не могу больше разделять результаты. Похоже, что 20% были просто случайностью. Помните, что вы устанавливаете функцию только один раз, поэтому создание экземпляра происходит только один раз, после чего функция устанавливается.
Сэнди Гиффорд
64

Ты можешь сделать

jQuery.fn.reverse = function() {
    return this.pushStack(this.get().reverse(), arguments);
}; 

с последующим

$(selector).reverse().each(...) 
Винай Саджип
источник
14
Этот подход был первоначально предложен Джоном Резигом (разработчиком jQuery) в списке рассылки jQuery .
Крис Кричит
20

Вот разные варианты для этого:

Во-первых : без JQuery:

var lis = document.querySelectorAll('ul > li');
var contents = [].map.call(lis, function (li) {
    return li.innerHTML;
}).reverse().forEach(function (content, i) {
    lis[i].innerHTML = content;
});

Демо здесь

... и с помощью jQuery:

Вы можете использовать это:

$($("ul > li").get().reverse()).each(function (i) {
    $(this).text( 'Item ' + (++i));
});

Демо здесь

Другой способ, используя также jQuery с reverse :

$.fn.reverse = [].reverse;
$("ul > li").reverse().each(function (i) {
    $(this).text( 'Item ' + (++i));
});

Это демо здесь .

Еще одна альтернатива - использовать length(количество элементов, соответствующих этому селектору) и перейти оттуда к indexкаждой итерации. Тогда вы можете использовать это:

var $li = $("ul > li");
$li.each(function (i) {
    $(this).text( 'Item ' + ($li.length - i));
});

Это демо здесь

Еще один вид, связанный с вышеупомянутым:

var $li = $("ul > li");
$li.text(function (i) {
    return 'Item ' + ($li.length - i);
});

Демо здесь

Sergio
источник
14

Я предпочитаю создавать обратный плагин, например

jQuery.fn.reverse = function(fn) {       
   var i = this.length;

   while(i--) {
       fn.call(this[i], i, this[i])
   }
};

Использование, например:

$('#product-panel > div').reverse(function(i, e) {
    alert(i);
    alert(e);
});
Джеймс Вестгейт
источник
Мне также нравится эта идея / код, но она не возвращает цепочечный объект (который перевернут), как ответы с более высоким голосом :) Тем не менее, безусловно, хороший метод для его установки
Ян
Вы правы. Не цепочка, поэтому не действительный плагин. Немного улучшили код, переместив декремент в состояние.
Джеймс Вестгейт
8

Если вы не хотите сохранять метод в jQuery.fn, вы можете использовать

[].reverse.call($('li'));
yurakis
источник
Это то, что я искал.
Александр Диксон
5

Нужно было сделать обратное на $ .each, поэтому я использовал идею Vinay:

//jQuery.each(collection, callback) =>
$.each($(collection).get().reverse(), callback func() {});

работал хорошо, спасибо

finklez
источник
Это не правильно; это должно быть просто так $.each(collection.reverse(), ...).
Софи Альперт,
@Ben Alpert - ваш комментарий некорректен, потому что коллекция не является истинным массивом
vsync
4

Вы не можете выполнять итерацию в обратном направлении с каждой функцией jQuery, но вы все равно можете использовать синтаксис jQuery.

Попробуйте следующее:

//get an array of the matching DOM elements   
var liItems = $("ul#myUL li").get();

//iterate through this array in reverse order    
for(var i = liItems.length - 1; i >= 0; --i)
{
  //do Something
}
Дэвид Андрес
источник
2

Я нашел Array.prototype.reverseнеудачный с объектами, так что я сделал новую функцию JQuery , чтобы использовать в качестве альтернативы: jQuery.eachBack(). Он выполняет итерацию, как обычно jQuery.each(), и сохраняет каждый ключ в массиве. Затем он инвертирует этот массив и выполняет обратный вызов исходного массива / объекта в порядке обращенных ключей.

jQuery.eachBack=function (obj, callback) {
    var revKeys=[]; $.each(obj,function(rind,rval){revKeys.push(rind);}); 
    revKeys.reverse();
    $.each(revKeys,function (kind,i){
        if(callback.call(obj[i], i, obj[i]) === false) {    return false;}
    });
    return obj;
}   
jQuery.fn.eachBack=function (callback,args) {
    return jQuery.eachBack(this, callback, args);
}
NRTRX
источник
-2

Я думаю тебе нужно

.parentsUntill()
Джимми М
источник
1
Почему вы так думаете? И как бы вы использовали это?
Берги
-2

Вы также можете попробовать

var arr = [].reverse.call($('li'))
arr.each(function(){ ... })
mattyfew
источник