Pergunta

estou a usar ColdFusion para preencher um modelo que inclui HTML listas (<ul>de).

A maioria deles não é tão longa, mas alguns têm comprimentos ridiculamente longos e podem realmente estar em 2 a 3 colunas.

Existe um HTML, ColdFusion ou talvez JavaScript (Tenho jQuery` disponível) maneira de fazer isso facilmente?Não vale a pena usar uma solução pesada e complicada para economizar rolagem.

Foi útil?

Solução

Então desenterrei este artigo de A List Apart Ganhos CSS:Listas de múltiplas colunas.Acabei usando a primeira solução, não é a melhor, mas as outras exigem o uso de HTML complexo que não pode ser gerado dinamicamente ou a criação de muitas classes personalizadas, o que poderia ser feito, mas exigiria muitos estilos in-line e possivelmente uma página enorme.

Outras soluções ainda são bem-vindas.

Outras dicas

Se o suporte ao Safari e Firefox for bom o suficiente para você, existe uma solução CSS:

ul {
  -webkit-column-count: 3;
     -moz-column-count: 3;
          column-count: 3;
  -webkit-column-gap: 2em;
     -moz-column-gap: 2em;
          column-gap: 2em;
}

Não tenho certeza sobre o Opera.

Não existe uma maneira CSS/HTML pura de conseguir isso, até onde eu sei.Sua melhor aposta seria fazer isso no pré-processamento (if list length > 150, split into 3 columns, else if > 70, split into 2 columns, else 1).

A outra opção, usando JavaScript (não estou familiarizado com o jQuery biblioteca especificamente) seria iterar pelas listas, provavelmente com base no fato de serem uma determinada classe, contar o número de filhos e, se for um número alto o suficiente, criar dinamicamente uma nova lista após a primeira, transferindo algum número de itens da lista para a nova lista.No que diz respeito à implementação das colunas, você provavelmente poderia flutuá-las para a esquerda, seguidas por um elemento que tivesse o estilo clear: left ou clear: both.

.column {
  float: left;
  width: 50%;
}
.clear {
  clear: both;
}
<ul class="column">
  <li>Item 1</li>
  <li>Item 2</li>
  <!-- ... -->
  <li>Item 49</li>
  <li>Item 50</li>
</ul>
<ul class="column">
  <li>Item 51</li>
  <li>Item 52</li>
  <!-- ... -->
  <li>Item 99</li>
  <li>Item 100</li>
</ul>
<div class="clear">

Eu fiz isso com jQuery - é multiplataforma e tem um mínimo de código.

Selecione o UL, clone-o e insira-o após o UL anterior.Algo como:

$("ul#listname").clone().attr("id","listname2").after()

Isso irá inserir uma cópia da sua lista após a anterior.Se a lista original for estilizada com float:left, eles deverão aparecer lado a lado.

Então você pode excluir os itens pares da lista da esquerda e os itens ímpares da lista da direita.

$("ul#listname li:even").remove();
$("ul#listname2 li:odd").remove();

Agora você tem uma lista de duas colunas da esquerda para a direita.

Para fazer mais colunas que você deseja usar .slice(begin,end) e/ou o :nth-child seletor.ou seja, por 21 LIs você poderia .slice(8,14) para criar um novo UL inserido após o UL original, selecione o UL original e exclua os lis selecionados com ul :gt(8).

Experimente o livro Bibeault/Katz sobre jQuery, é um ótimo recurso.

Aqui está uma variação Thumbkin's exemplo (usando Jquery):

var $cat_list = $('ul#catList'); // UL with all list items.
var $cat_flow = $('div#catFlow'); // Target div.
var $cat_list_clone = $cat_list.clone(); // Clone the list.
$('li:odd', $cat_list).remove(); // Remove odd list items.
$('li:even', $cat_list_clone).remove(); // Remove even list items.
$cat_flow.append($cat_list_clone); // Append the duplicate to the target div.

Obrigado Thumbkin!

O código JavaScript a seguir funciona apenas em Spidermonkey e Rhino e opera em nós E4X - ou seja, isso é útil apenas para JavaScript do lado do servidor, mas pode fornecer a alguém um ponto de partida para fazer uma versão jQuery.(Tem sido muito útil para mim no lado do servidor, mas não precisei dele no cliente o suficiente para realmente construí-lo.)

function columns(x,num) {
    num || (num = 2);
    x.normalize();

    var cols, i, j, col, used, left, len, islist;
    used = left = 0;
    cols = <div class={'columns cols'+num}></div>;

    if((left = x.length())==1)
        left = x.children().length();
    else
        islist = true;

    for(i=0; i<num; i++) {
        len = Math.ceil(left/(num-i));
        col = islist ? new XMLList
                     : <{x.name()}></{x.name()}>;

        if(!islist && x['@class'].toString())
            col['@class'] = x['@class'];

        for(j=used; j<len+used; j++)
            islist ? (col += x[j].copy()) 
                   : (col.appendChild(x.child(j).copy()));

        used += len;
        left -= len;
        cols.appendChild(<div class={'column'+(i==(num-1) ? 'collast' : '')}>{col}</div>);
    }
    return cols;
}

Você chama isso de columns(listNode,2) para duas colunas, e acontece:

<ul class="foo">
  <li>a</li>
  <li>b</li>
  <li>c</li>
</ul>

em:

<div class="columns cols2">
  <div class="column">
    <ul class="foo">
      <li>a</li>
      <li>b</li>
    </ul>
  </div>
  <div class="column collast">
    <ul class="foo">
      <li>c</li>
    </ul>
  </div>
</div>

Ele deve ser usado com CSS assim:

div.columns {
    overflow: hidden;
    _zoom: 1;
}

div.columns div.column {
    float: left;
}

div.cols2 div.column {
    width: 47.2%;
    padding: 0 5% 0 0;
}

div.cols3 div.column {
    width: 29.8%;
    padding: 0 5% 0 0;
}

div.cols4 div.column {
    width: 21.1%;
    padding: 0 5% 0 0;
}

div.cols5 div.column {
    width: 15.9%;
    padding: 0 5% 0 0;
}

div.columns div.collast {
    padding: 0;
}

O que a maioria das pessoas esquece é que, ao flutuar <li/> itens, todos os itens devem ter a mesma altura ou as colunas começarão a ficar fora de sintonia.

Como você está usando uma linguagem do lado do servidor, minha recomendação seria usar CF para dividir a lista em três arrays.Então você pode usar um externo ul para embrulhar os 3 internos ul igual a:

<cfset thelist = "1,2,3,4,5,6,7,8,9,10,11,12,13">  
<cfset container = []>  
<cfset container[1] = []>  
<cfset container[2] = []>  
<cfset container[3] = []>  

<cfloop list="#thelist#" index="i">  
    <cfif i mod 3 eq 0>  
        <cfset arrayappend(container[3], i)>  
    <cfelseif i mod 2 eq 0>  
        <cfset arrayappend(container[2], i)>  
    <cfelse>  
        <cfset arrayappend(container[1], i)>  
    </cfif>  
</cfloop>  

<style type="text/css"> 
    ul li { float: left; }  
    ul li ul li { clear: left; }  
</style>  

<cfoutput>  
<ul>  
    <cfloop from="1" to="3" index="a">  
    <li>  
        <ul>  
            <cfloop array="#container[a]#" index="i">  
            <li>#i#</li>  
            </cfloop>  
        </ul>  
    </li>  
    </cfloop>  
</ul>  
</cfoutput>

Usando uma operação de módulo, você pode dividir rapidamente sua lista em várias listas inserindo um </ul><ul> durante o seu loop.

<cfset numberOfColumns = 3 />
<cfset numberOfEntries = 34 />
<ul style="float:left;">
    <cfloop from="1" to="#numberOfEntries#" index="i">
        <li>#i#</li>
            <cfif NOT i MOD ceiling(numberOfEntries / numberOfColumns)>
                </ul>
                <ul style="float:left;">
            </cfif>
    </cfloop>
</ul>

Usar ceiling() em vez de round() para garantir que você não tenha valores extras no final da lista e que a última coluna seja mais curta.

Para gerar a lista em várias tags agrupadas, você pode fazer um loop desta maneira.

<cfset list="1,2,3,4,5,6,7,8,9,10,11,12,13,14">
<cfset numberOfColumns = "3">

<cfoutput>
<cfloop from="1" to="#numberOfColumns#" index="col">
  <ul>
  <cfloop from="#col#" to="#listLen(list)#" index="i" step="#numberOfColumns#">
    <li>#listGetAt(list,i)#</li>
  </cfloop>
  </ul>
</cfloop>
</cfoutput>

Aqui está outra solução que permite listas em colunas no seguinte estilo:

1.      4.      7.       10.
2.      5.      8.       11.
3.      6.      9.       12.

(mas é javascript puro e requer jQuery, sem substituto)

O seguinte contém algum código que modifica o protótipo Array para fornecer uma nova função chamada 'chunk' que divide qualquer Array em pedaços de um determinado tamanho.A seguir está uma função chamada 'buildColumns' que usa uma string de seletor UL e um número usado para designar quantas linhas suas colunas podem conter.(Aqui está um JSFiddle funcional)

$(document).ready(function(){
    Array.prototype.chunk = function(chunk_size){
        var array = this,
            new_array = [],
            chunk_size = chunk_size,
            i,
            length;

        for(i = 0, length = array.length; i < length; i += chunk_size){
            new_array.push(array.slice(i, i + chunk_size));
        }
        return new_array;
    }

    function buildColumns(list, row_limit) {
        var list_items = $(list).find('li').map(function(){return this;}).get(),
        row_limit = row_limit,
        columnized_list_items = list_items.chunk(row_limit);

        $(columnized_list_items).each(function(i){
            if (i != 0){
                var item_width = $(this).outerWidth(),
                    item_height = $(this).outerHeight(),
                    top_margin = -((item_height * row_limit) + (parseInt($(this).css('margin-top')) * row_limit)),
                    left_margin = (item_width * i) + (parseInt($(this).css('margin-left')) * (i + 1));

                $(this[0]).css('margin-top', top_margin);
                $(this).css('margin-left', left_margin);
            }
        });
    }

    buildColumns('ul#some_list', 5);
});

O Flexbox pode ser usado para agrupar itens nas direções de linha e coluna.

A idéia principal é definir o flex-direction no contêiner para row ou column.

Observação:Hoje em dia suporte ao navegador é muito bom.

VIOLINO

(Amostra de marcação retirada de este antigo artigo de 'lista separada')

ol {
  display: flex;
  flex-flow: column wrap; /* flex-direction: column */
  height: 100px; /* need to specify height :-( */
}
ol ~ ol {
  flex-flow: row wrap; /* flex-direction: row */
  max-height: auto; /* override max-height of the column direction */
}
li {
  width: 150px;
}
a {
  display: inline-block;
  padding-right: 35px;
}
<p>items in column direction</p>
<ol>
  <li><a href="#">Aloe</a>
  </li>
  <li><a href="#">Bergamot</a>
  </li>
  <li><a href="#">Calendula</a>
  </li>
  <li><a href="#">Damiana</a>
  </li>
  <li><a href="#">Elderflower</a>
  </li>
  <li><a href="#">Feverfew</a>
  </li>
  <li><a href="#">Ginger</a>
  </li>
  <li><a href="#">Hops</a>
  </li>
  <li><a href="#">Iris</a>
  </li>
  <li><a href="#">Juniper</a>
  </li>
  <li><a href="#">Kava kava</a>
  </li>
  <li><a href="#">Lavender</a>
  </li>
  <li><a href="#">Marjoram</a>
  </li>
  <li><a href="#">Nutmeg</a>
  </li>
  <li><a href="#">Oregano</a>
  </li>
  <li><a href="#">Pennyroyal</a>
  </li>
</ol>
<hr/>
<p>items in row direction</p>
<ol>
  <li><a href="#">Aloe</a>
  </li>
  <li><a href="#">Bergamot</a>
  </li>
  <li><a href="#">Calendula</a>
  </li>
  <li><a href="#">Damiana</a>
  </li>
  <li><a href="#">Elderflower</a>
  </li>
  <li><a href="#">Feverfew</a>
  </li>
  <li><a href="#">Ginger</a>
  </li>
  <li><a href="#">Hops</a>
  </li>
  <li><a href="#">Iris</a>
  </li>
  <li><a href="#">Juniper</a>
  </li>
  <li><a href="#">Kava kava</a>
  </li>
  <li><a href="#">Lavender</a>
  </li>
  <li><a href="#">Marjoram</a>
  </li>
  <li><a href="#">Nutmeg</a>
  </li>
  <li><a href="#">Oregano</a>
  </li>
  <li><a href="#">Pennyroyal</a>
  </li>
</ol>

Como tive o mesmo problema e não consegui encontrar nada "limpo", pensei em postar minha solução.Neste exemplo eu uso um inverso while loop para que eu possa usar splice em vez de slice.A vantagem agora é que splice() só precisa de um índice e um intervalo, enquanto slice() precisa de um índice e o total.Este último tende a se tornar difícil durante o loop.

A desvantagem é que preciso inverter a pilha durante o acréscimo.

Exemplo:

colunas = 4;liCont = 35

for loop com fatia = [0, 9];[9, 18];[18, 27];[27, 35]

invertido enquanto com emenda = [27, 8];[18, 9];[9, 9];[0, 9]

Código:

// @param (list): a jquery ul object
// @param (cols): amount of requested columns
function multiColumn (list, cols) {
    var children = list.children(),
        target = list.parent(),
        liCount = children.length,
        newUl = $("<ul />").addClass(list.prop("class")),
        newItems,
        avg = Math.floor(liCount / cols),
        rest = liCount % cols,
        take,
        stack = [];

    while (cols--) {
        take = rest > cols ? (avg + 1) : avg;
        liCount -= take;

        newItems = children.splice(liCount, take);
        stack.push(newUl.clone().append(newItems));
    }

    target.append(stack.reverse());
    list.remove();
}

Você pode tentar isso para converter em cols.

CSS:

ul.col {
    width:50%;
    float:left;
}

div.clr {
    clear:both;
}

Parte HTML:

<ul class="col">
    <li>Number 1</li>
    <li>Number 2</li>

    <li>Number 19</li>
    <li>Number 20</li>
</ul>
<ul class="col">
    <li>Number 21</li>
    <li>Number 22</li>

    <li>Number 39</li>
    <li>Number 40</li>
</ul>

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top