Envolva a cada 3 divs em um div

86

É possível usar nth-childseletores para envolver 3 divs usando.wrapAll ? Não consigo descobrir a equação correta.

então...

<div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
</div>

torna-se...

<div>
   <div class="new">
        <div></div>
        <div></div>
        <div></div>
   </div>
   <div class="new">
        <div></div>
        <div></div>
        <div></div>
   </div>
</div>
csbourne
fonte
2
gist.github.com/3181731 Um bom plugin jQuery para fazer exatamente isso. Espero que seja útil.
iMoses

Respostas:

180

Você pode fazer isso com .slice()este:

var divs = $("div > div");
for(var i = 0; i < divs.length; i+=3) {
  divs.slice(i, i+3).wrapAll("<div class='new'></div>");
}

Você pode experimentar uma demonstração aqui , tudo o que estamos fazendo aqui é pegar os elementos que você deseja embrulhar e fazer um loop através deles, fazendo um .wrapAll()em lotes de 3 e passando para os próximos 3 etc. no entanto, muitos são deixados no final, por exemplo, 3, 3, 3, 2 se for esse o caso.

Nick Craver
fonte
Eu faria disso uma função e do número de divs agrupados um argumento. Algo como applyDivGrouping (divs, divsPerGroup);
Stefan Kendall
Uau! Obrigado pela resposta rápida. Algumas coisas ... Então, apenas para esclarecimento - isso não é possível usando o n-ésimo filho? & .. Como um completo novato em jQuery - como faço para que isso funcione? Devo envolvê-lo em um jQuery (function ($) ...? Muito obrigado
csbourne
@csbourne - Não :nth-child()se presta bem a isso, quanto a chamar isso, apenas embrulhe-o em um $(function() { });se quiser executá-lo document.ready, caso contrário, chame quando quiser executá-lo :)
Nick Craver
Obrigado Nick pela ajuda e orientação fantásticas - funciona perfeitamente.
csbourne
3
@Fahad, de acordo com a lógica do NickCraver, você pode simplesmente editar um pequeno trecho de código var divs = $("div > .classname");OU var divs = $("div .classname");Obrigado
23

Escrevi uma função de bloco genérica que torna isso muito fácil de fazer:

$.fn.chunk = function(size) {
    var arr = [];
    for (var i = 0; i < this.length; i += size) {
        arr.push(this.slice(i, i + size));
    }
    return this.pushStack(arr, "chunk", size);
}

$("div > div").chunk(3).wrap('<div class="new"></div>');

Ja͢ck
fonte
8

O Plugin

$(function() {
    $.fn.EveryWhat = function(arg1) {
        var arr = [];
        if($.isNumeric(arg1)) {
            $.each(this, function(idx, item) {
                var newNum = idx + 1;
                if(newNum%arg1 == 0)
                arr.push(item);
            });
        }
        return this.pushStack(arr, "EveryWhat", "");
    }
});

Como usá-lo.

Chame EveryWhat()o elemento e coloque um número para cada elemento que você gostaria de coletar.

$("div").EveryWhat(2).wrapInner('<div class="new" />');

as citações do wrapinner devem ter um formato adequado <div class="new" />com classe e tag de fechamento. Stackoverflow me impede de mostrar o que parece, mas aqui está um link de um div de fechamento automático.

Como deve ser

Isso envolverá todos os outros números que você especificou. Estou usando o jquery 1.8.2. então lembre-se de usar a chamada do seletor EveryWhat(3)e um número para cada vez. Claro, colocá-lo no final da página ou envolvê-lo em um

$(document).ready(function() {  
    //place above code here
});

Você pode usar cada enésima e, .wrapInner('<div class="new" />')em seguida, para os mesmos resultados.

Alex Williams
fonte
1
Você já pode fazer isso $('div > div:nth-child(3n)')e não resulta em dois grupos de três elementos.
Ja͢ck de
7

Aqui está uma versão mais utilizável de Nick acima:

window.WrapMatch = function(sel, count, className){
  for(var i = 0; i < sel.length; i+=count) {
    sel.slice(i, i+count).wrapAll('<div class="'+className+'" />');
  }
}

Você usaria isso como:

var ele = $('#menu > ul > li'); 
window.WrapMatch(ele, 5, 'new-class-name');

janela deve ser substituída pelo seu namespace Handlers, é claro.

Atualizado: uma versão ligeiramente melhor que aproveita jQuery

(function($){
  $.fn.wrapMatch = function(count, className) {
    var length = this.length;
    for(var i = 0; i < length ; i+=count) {
      this.slice(i, i+count).wrapAll('<div '+((typeof className == 'string')?'class="'+className+'"':'')+'/>');
    }
    return this;
  }; 
})(jQuery);

Use como:

$('.list-parent li').wrapMatch(5,'newclass');

O segundo parâmetro para o nome do wrapper é opcional.

Pat
fonte
1
$(function() {
    $.fn.WrapThis = function(arg1, arg2) { /*=Takes 2 arguments, arg1 is how many elements to wrap together, arg2 is the element to wrap*/

        var wrapClass = "column"; //=Set class name for wrapping element

        var itemLength = $(this).find(arg2).length; //=Get the total length of elements
        var remainder = itemLength%arg1; //=Calculate the remainder for the last array
        var lastArray = itemLength - remainder; //=Calculate where the last array should begin

        var arr = [];

        if($.isNumeric(arg1))
        {
            $(this).find(arg2).each(function(idx, item) {
                var newNum = idx + 1;

                if(newNum%arg1 !== 0 && newNum <= lastArray){
                    arr.push(item);
                }
                else if(newNum%arg1 == 0 && newNum <= lastArray) {
                    arr.push(item);
                    var column = $(this).pushStack(arr);
                    column.wrapAll('<div class="' + wrapClass + '"/>'); //=If the array reaches arg1 setting then wrap the array in a column
                    arr = [];
                }
                else if(newNum > lastArray && newNum !== itemLength){ //=If newNum is greater than the lastArray setting then start new array of elements
                    arr.push(item);
                }
                else { //=If newNum is greater than the length of all the elements then wrap the remainder of elements in a column
                    arr.push(item);
                    var column = $(this).pushStack(arr);
                    column.wrapAll('<div class="' + wrapClass + '"/>');
                    arr = []
                }
            });
        }
    }
});

Peguei a ideia do plugin de Kyle e a estendi para quebrar automaticamente e usar dois argumentos. Não funcionou para mim no início, mas eu o coloquei em execução com algumas edições e adições ao código.

Para invocar a função, basta usar o elemento pai do que você deseja agrupar e definir seus argumentos da seguinte maneira.

$('#container').WrapThis(5, 'li');

O primeiro argumento é quantos elementos você deseja agrupar e o segundo argumento é o tipo de elemento que você gostaria de agrupar.

Você pode alterar a classe do elemento de empacotamento na função principal da variável wrapClass.

PixelPrecision
fonte
0

Eu preparei esta resposta para outra pergunta que era uma duplicata desta. Então, talvez minha variante seja útil para alguém:

Acho que a solução para envolver todos os três elementos é:

var $lines = $('.w-col'), // All Dom elelements with class .w-col
     holder = []; //Collect DOM elelements

$lines.each(function (i, item) {
  holder.push(item);

  if (holder.length === 3) {
    $(holder).wrapAll('<div class="w-row" />');
    holder.length  = 0;
  }
});

$(holder).wrapAll('<div class="w-row" />'); //Wrap last elements with div(class=w-row)

Escrevi o mesmo código em jsbin com algumas melhorias http://jsbin.com/necozu/17/ ou http://jsbin.com/necozu/16/

Chekit
fonte