Я использую ColdFusion
для заполнения шаблона, который включает в себя неупорядоченные списки HTML <ul>
.
Большинство из них не такие длинные, но некоторые имеют смехотворно большую длину и могут стоять в 2-3 столбца.
Есть ли способ сделать это с помощью HTML, ColdFusion или JavaScript (я принимаю решения jQuery)? Это не стоит слишком сложного решения, чтобы сэкономить на прокрутке.
Насколько я знаю, не существует чистого способа 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">
Итак, я выкопал эту статью из CSS List Swag: Многостолбцовые списки . Я закончил тем, что использовал первое решение, оно не самое лучшее, но другие требуют либо использования сложного HTML, который не может быть сгенерирован динамически, либо создания большого количества пользовательских классов, которые могли бы быть выполнены, но потребовали бы множества встроенных стилей и возможно огромная страница.
Другие решения все еще приветствуются.
Если поддержка 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;
}
Я не уверен насчет Оперы.
Чтобы вывести список в несколько сгруппированных тегов, вы можете выполнить цикл таким образом.
<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>
Следующий код 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;
}
Я сделал это с помощью jQuery - это кроссплатформенность и минимум кода.
Выберите UL, клонируйте его и вставьте после предыдущего UL. Что-то типа:
$("ul#listname").clone().attr("id","listname2").after()
Это вставит копию вашего списка после предыдущего. Если исходный список имеет стиль с плавающей точкой: слева, они должны отображаться рядом.
Затем вы можете удалить четные элементы из списка слева и нечетные элементы из списка справа.
$("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)
.
Попробуйте книгу Bibeault / Katz на jQuery, это отличный ресурс.
Большинство людей забывают о том, что при перемещении <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()
чтобы убедиться, что у вас нет дополнительных значений в конце списка и что последний столбец самый короткий.
Вот вариант примера 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.
Спасибо, Thumbkin!
Вот еще одно решение, которое позволяет создавать колонные списки в следующем стиле:
1. 4. 7. 10.
2. 5. 8. 11.
3. 6. 9. 12.
(но это чистый javascript и требует jQuery, без отступления)
Следующее содержит некоторый код, который модифицирует прототип Array, чтобы дать новую функцию с именем '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
для цикла со срезом = [0, 9]; [9, 18]; [18, 27]; [27, 35]
поменять местами при сращивании = [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>
Flexbox можно использовать для упаковки элементов в направлениях строк и столбцов.
Основная идея состоит в том, чтобы установить flex-direction
на контейнере либо либо, row
либо column
.
NB: В настоящее время поддержка браузеров довольно хорошая.
FIDDLE
(Образец разметки взят из этой старой статьи "list обособленно" )
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>