Объединение списков в столбцы

Обернуть списки в столбцы

Я использую ColdFusion для заполнения шаблона, который включает в себя неупорядоченные списки HTML (<ul>ы).

Большинство из них не такие уж длинные, но некоторые имеют смехотворно большую длину и могли бы поместиться в 2-3 колонки

Есть ли способ сделать это легко с помощью HTML, ColdFusion или, может быть, JavaScript (я принимаю решения jQuery)? Не стоит использовать слишком сложное тяжеловесное решение, чтобы сэкономить время на прокрутке.

Этот плагин jquery вам чем-то полезен?

Ознакомьтесь с плагином Columnizer jQuery.

13 ответов

Итак, я откопал эту статью из A List Apart CSS Swag: Многоколоночные списки. Я в итоге использовал первое решение, оно не лучшее, но другие требуют либо использования сложного HTML, который нельзя сгенерировать динамически, либо создания множества пользовательских классов, что можно сделать, но это потребует множества встроенных стилей и, возможно, огромной страницы.

Однако другие решения по-прежнему приветствуются.

Печально, что спустя два года все еще нет чистого способа сделать это. Спасибо IE.

+1 Я считаю, что метод 6 — самый чистый подход: его можно настроить так, чтобы не использовать разметку и использовать очень мало CSS.

Если вас устраивает поддержка Safari и Firefox, есть решение на основе CSS:

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

Я не уверен насчет Opera.

Я тоже использую это решение, но у меня пропадают маркеры списка... даже сброс их или попытка использовать изображение не помогает. Кто-нибудь?

97.8% браузерная поддержка сейчас. Узнайте больше о опции столбцов в CSS

Объединение списков в столбцы

Насколько мне известно, чистого способа CSS/HTML для этого не существует. Лучшим вариантом будет сделать это в предварительной обработке (if list length > 150, split into 3 columns, else if > 70, split into 2 columns, else 1).

Другой вариант, с использованием JavaScript (я не знаком конкретно с библиотекой jQuery), — это перебирать списки, вероятно, основываясь на том, что они принадлежат к определенному классу, подсчитывать количество дочерних элементов и, если оно достаточно велико, динамически создавать новый список после первого, перенося некоторое количество элементов списка в новый список. Что касается реализации столбцов, вы, вероятно, могли бы сделать их плавающими влево, а затем добавить элемент со стилем clear: left или 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">

Я сделал это с помощью jQuery — он кроссплатформенный и требует минимум кода.

Выберите UL, клонируйте его и вставьте после предыдущего UL. Что-то вроде:

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

Это вставит копию вашего списка после предыдущего. Если исходный список оформлен с помощью float:left, они должны отображаться рядом.

Затем вы можете удалить четные элементы из левого списка и нечетные элементы из правого списка.

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

Теперь у вас есть список из двух столбцов слева направо.

Чтобы сделать больше столбцов, вам нужно будет использовать .slice(begin,end) и/или :nth-child селектор. Например, для 21 LI вы можете .slice(8,14) чтобы создать новый UL, вставленный после вашего исходного UL, затем выбрать исходный UL и удалить li, выбранные с помощью ul :gt(8).

Попробуйте книгу Бибо/Каца по jQuery — это отличный ресурс.

Вот вариация примера Thumbkin (с использованием 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.

Следующий код JavaScript работает только в Spidermonkey и Rhino и работает на узлах E4X, то есть он полезен только для серверного JavaScript, но может стать отправной точкой для создания версии jQuery. (Мне он очень пригодился на стороне сервера, но на клиенте он мне не был нужен настолько сильно, чтобы действительно его создать.)

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;
}

Вы вызываете его как columns(listNode,2) для двух столбцов, и он превращается:

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

в:

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

Его следует использовать с CSS следующим образом:

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;
}

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

Поскольку вы используете серверный язык, я бы рекомендовал использовать CF для разделения списка на 3 массива. Затем вы можете использовать внешний, ul чтобы обернуть 3 внутренних ul, например:

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

Используя операцию по модулю, вы можете быстро разделить свой список на несколько списков, вставив </ul><ul> во время цикла.

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

Используйте ceiling() вместо round(), чтобы убедиться, что в конце списка нет лишних значений и последний столбец самый короткий.

Flexbox можно использовать для переноса элементов как в строковом, так и в столбцовом направлении.

Основная идея — установить для flex-direction контейнера значение row или column.

Примечание: В настоящее время поддержка браузеров довольно хороша.

СКРИПКА

(Пример разметки взят из этой старой статьи «Список отдельно»)

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>

Чтобы вывести список в несколько сгруппированных тегов, можно выполнить цикл следующим образом.

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

Вот еще одно решение, позволяющее создавать столбчатые списки в следующем стиле:

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

(но это чистый javascript и требует jQuery, без резервного варианта)

Ниже приведен код, который изменяет прототип массива, чтобы получить новую функцию под названием 'chunk', которая разбивает любой заданный массив на части заданного размера. Далее следует функция под названием 'buildColumns', которая принимает строку селектора UL и число, используемое для обозначения того, сколько строк могут содержать ваши столбцы. (Вот работающий JSFiddle)

$(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);
});

Поскольку у меня была та же проблема, и я не смог найти ничего "чистого", я думал, что опубликовал свое решение. В этом примере я использую обратный while цикл, поэтому я могу использовать splice вместо slice. Преимущество теперь в том, что splice() нужны только индекс и диапазон, тогда как slice() нужны индекс и общая сумма. Последнее, как правило, становится сложным при выполнении цикла.

Недостаток в том, что при добавлении мне приходится переворачивать стек.

Пример:

cols = 4; liCount = 35

for loop with slice = [0, 9]; [9, 18]; [18, 27]; [27, 35]

reversed while with splice = [27, 8]; [18, 9]; [9, 9]; [0, 9]

Код:

// @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();
}

Вы можете попробовать это для конвертации в столбцы.

CSS-код:

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

div.clr {
    clear:both;
}

Часть 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>

Как в JS использовать клавиатуру для перемещения в раскрывающимся меню

Использование клавиатуры для перемещения в раскрывающимся меню

Как в JavaScript использовать клавиатуру для перемещения по опциям в раскрывающимся меню У меня есть специально созданный динамический раскрывающийся список на основе ajax [div]. У меня есть поле [ввода], которое; onkeyup , запускает поиск Ajax, который возвращает результаты в div и возвращается с помощью innerHTML . Все div имеют основные моменты onmouseover , поэтому типичный успешный поиск дает следующую структуру (простите за полукод): [input] [div id=results] //this gets overwritten contantly by my AJAX function [div id=result1 onmouseover=highlight onclick=input.value=result1] [div id=result2 onmouseover=highlight onclick=input.value=result2] [div id=result2 onmouseover=highlight onclick=input.value=result2] [/div] Он работает. Однако мне не хватает важных функций обычных HTML-элементов. Я не могу нажимать или поднимать клавиатуру между «опциями». Я знаю, что JavaScript обрабатывает события клавиатуры, но; Я не смог найти хорошего гида. Конечно, последующий…

Подробнее »

Http Auth в букмарклете Firefox

Http Auth в букмарклете Firefox

Я пытаюсь создать букмарклет для размещения закладок del_icio_us в отдельной учетной записи. Я тестировал это из командной строки, например: wget -O - --no-check-certificate \ "https _ seconduser:thepassword@ api _ del _ icio _ us/v1/posts/add?url=http _ seet _ dk&description=test" Это прекрасно работает. Затем я захотел создать закладку в своем Firefox. Я погуглил, нашел кусочки и в итоге получил следующее: javascript:void( open('https _ seconduser:password@ api _ del _ icio _ us/v1/posts/add?url=' +encodeURIComponent(location.href) +'&description='+encodeURIComponent(document.title)…

Подробнее »

Как я могу превратить строку HTML в объект DOM в расширении FF?

Превратите строку HTML в объект DOM

Как я могу превратить строку HTML в объект DOM в расширении Firefox? Я загружаю веб-страницу (HTML-тег-суп) с XMLHttpRequest и хочу взять выходные данные и превратить их в объект DOM, к которому затем можно будет выполнять запросы XPATH. Как преобразовать строку в объект DOM? Похоже, что общее решение состоит в том, чтобы создать скрытый iframe и бросить в него содержимое строки. Ходили разговоры об обновлении DOMParser для поддержки text/html, но начиная с Firefox…

Подробнее »