JQuery .each () para trás

442

Estou usando o JQuery para selecionar alguns elementos em uma página e movê-los no DOM. O problema que estou tendo é que preciso selecionar todos os elementos na ordem inversa que o JQuery naturalmente deseja selecioná-los. Por exemplo:

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

Quero selecionar todos os itens de li e usar o comando .each () neles, mas quero começar com o Item 5, depois o Item 4 etc. Isso é possível?

Jack Mills
fonte

Respostas:

681
$($("li").get().reverse()).each(function() { /* ... */ });
Joe Chung
fonte
10
Deve ser importante observar que o índice não é revertido; portanto, se você deseja apenas executar os três últimos, por exemplo, não pode esperar <li>Item 5</li>ter um índice de 0. #
pathfinder
8
para David Andres: pode ser que você tenha esquecido de adicionar $ () adicional antes de li. Eu cometi esse erro e recebi "função indefinida".
Michal - wereda-net
2
@ DavidAndres: Você perdeu o .get()que transforma o array empacotado jQuery em um array JS comum. A matriz JS possui .reverse().
stackular
1
Cuidado: Array.reverse()ambos modificam a matriz no lugar e retornam a matriz invertida. Portanto, esta solução depende, na verdade, de $ (). Get () retornando uma nova matriz, e não uma referência a alguma matriz interna jQuery ou DOM. Provavelmente uma aposta segura dentro dos limites deste exemplo. Advertência Coder.
Bob Stein
404

Apresento a você a maneira mais limpa de sempre, na forma do menor plugin jquery do mundo:

jQuery.fn.reverse = [].reverse;

Uso:

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

- Todo o crédito a Michael Geary em seu post aqui: http://www.mail-archive.com/[email protected]/msg04261.html

Waldo Hampton
fonte
Ok, eu concordo que é legal, mas por que funciona? Você está copiando (vagamente) Array.prototype.reverse para jQuery.prototype.reverse e, quando é chamado, o thisobjeto tem propriedades numéricas e uma propriedade length, para que o javascript possa indexá-lo e aplicar uma função de matriz a uma instância do jQuery?
amigos estão dizendo sobre mlhDev
5
Deve ser importante observar que o índice não é revertido; portanto, se você deseja apenas executar os três últimos, por exemplo, não pode esperar <li>Item 5</li>ter um índice de 0. #
pathfinder
1
@ Matthew - Sim, você entende direito. Esse código realmente copia uma referência para Array.prototype.reverseover to jQuery.prototype.reverse. Muitos dos métodos de matriz JavaScript funcionarão bem em qualquer objeto que pareça suficientemente com uma matriz - isto é, se o objeto tiver .lengthpropriedades de índice numérico. reversenão depende de alguma representação interna da matriz; ele acessa a matriz usando o normal .length, etc. [0], [1]assim como o código da matriz que você escreveria.
Michael Geary
8
Não é um desperdício instanciar uma nova matriz apenas para obter seu reversemétodo? Não seria mais rápido copiar apenas uma referência à função Array.prototype.reverse, por exemplo jQuery.fn.reverse = Array.prototype.reverse;? Não é tão curto e "inteligente", mas mais eficiente? É certo que este provavelmente será imperceptível em navegadores extremamente rápidas de hoje, mas ainda assim ...
zachelrath
3
@zachelrath Sim, não posso mais ter nenhum tipo de separação entre os resultados. Parece que 20% foi apenas um acaso. Lembre-se de que você está definindo a função apenas uma vez; portanto, a instanciação acontece apenas uma vez; a partir daí, a função é definida.
Sandy Gifford
64

Você pode fazer

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

Seguido por

$(selector).reverse().each(...) 
Vinay Sajip
fonte
14
Essa abordagem foi originalmente proposta por John Resig (desenvolvedor do jQuery) na lista de discussão do jQuery .
precisa
20

Aqui estão opções diferentes para isso:

Primeiro : sem 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;
});

Demonstração aqui

... e com jQuery:

Você pode usar isto:

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

Demonstração aqui

Outra maneira, usando também o jQuery com reverse é:

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

Esta demonstração aqui .

Mais uma alternativa é usar o length(número de elementos correspondentes a esse seletor) e descer a partir daí usando indexcada iteração. Então você pode usar isso:

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

Esta demonstração aqui

Mais um , meio que relacionado ao anterior:

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

Demonstração aqui

Sergio
fonte
14

Eu prefiro criar um plug-in reverso, por exemplo

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

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

Uso por exemplo:

$('#product-panel > div').reverse(function(i, e) {
    alert(i);
    alert(e);
});
James Westgate
fonte
Também gosto dessa ideia / código, mas ela não retorna um objeto encadeado (que é revertido) como as respostas com maior número de votos :) Ainda assim, definitivamente é um bom método para a maneira como é configurado
Ian
Você está certo. Não pode ser encadeado, portanto, não é um plugin válido. Porém, melhoramos um pouco o código, movendo o decrementador para a condição.
James Westgate
8

Se você não deseja salvar o método no jQuery.fn, pode usar

[].reverse.call($('li'));
yurakis
fonte
Era isso que eu estava procurando.
Alexander Dixon
5

Precisava fazer um reverso em $ .each, então usei a idéia do Vinay:

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

funcionou bem, obrigado

finklez
fonte
Isso não está certo; isso deveria ser apenas $.each(collection.reverse(), ...).
Sophie Alpert
@ Ben Alpert - seu comentário é falho, porque coleção não é um verdadeiro leque
vsync
4

Você não pode iterar para trás com cada função do jQuery, mas ainda pode aproveitar a sintaxe do jQuery.

Tente o seguinte:

//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
}
David Andres
fonte
2

Achei Array.prototype.reversemal sucedida com objetos, então eu fiz uma nova função jQuery para usar como uma alternativa: jQuery.eachBack(). Ele itera como o normal jQuery.each()faria e armazena cada chave em uma matriz. Em seguida, ele reverte essa matriz e executa o retorno de chamada na matriz / objeto original na ordem das teclas invertidas.

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

Eu acho que você precisa

.parentsUntill()
Jimmy M
fonte
1
Por que você pensa isso? E como você o usaria?
Bergi
-2

Você também pode tentar

var arr = [].reverse.call($('li'))
arr.each(function(){ ... })
mattyfew
fonte