使用 CSS 创建流畅的、多列的、垂直排序的列表






4.83/5 (23投票s)
使用嵌套的浮动 div 创建一个流畅的、多列的、垂直排序的列表
引言
本文介绍了一种新颖的解决方案,用于创建动态的、多列的、垂直排序的列表,该列表可根据可用宽度流畅地换行,同时保留项目的排序。
背景
使用 HTML 创建单列垂直列表非常简单,只需要使用 UL
和 LI
标签,甚至 BR
标签即可。通过一些服务器端代码,可以将列表分解成大小相等的块,然后将其插入到多列表格或一系列 div
s 中,从而扩展到固定数量的列。然而,我们最近有一个需求,需要创建一个流畅的、多列的垂直列表,该列表通过按需换行来高效地利用可用空间。在网上搜索答案,得到的建议是使用 JavaScript 或等待 CSS3 列功能获得更广泛的支持!我们不想这样做,所以我们提出了我们认为是一种新颖的方法。
Using the Code
解决此问题的一种方法是使用固定宽度的 float
。
<div style="float:left;width:120px;">Archery<br />Ballooning<br />Biking</div>
<div style="float:left;width:120px;">Bungee Jumping<br />Coasteering<br />Dancing</div>
<div style="float:left;width:120px;">Driving<br />Gliding<br />Motor Racing</div>
<div style="float:left;width:120px;">Murder Mystery<br />Off-Roading<br />Paintballing</div>
当 width
足够时(由虚线矩形指示),此代码将呈现如下:
但是,当可用 width
变小时,列表会以一种方式换行,虽然水平排序可能保留,但垂直排序不会保留,直到完全换行。
此外,如果一些浮动的 div
s 高度不同,即使是水平排序也会中断,因为换行的 float
s 会被“智能地”放置。
问题在于我们无法控制 float
s 的换行顺序。如果我们能以某种方式告诉第二个 float
在第四个 float
之前换行到第一个下方,而不是让第四个换行到第一个、第二个和第三个下方,那我们就成功了。然而,总是最后一个 float
被换行。
我们解决此问题的方法是使用嵌套的 float
s,并设置 float
s 的 width
和 min-width
属性,以限制列表块的换行顺序。
我们原型化的 float
现在看起来是这样的:
<div style="float:left;width:50%;min-width:110px;"> CONTENT </div>
这些被嵌套在一起,使得每对相邻的列表项或项目块(每个都在一个浮动的 div
中)都被包含在一个超级的浮动的 div
中。
<div style="float:left;width:50%;min-width:110px;">
<div style="float:left;width:50%;min-width:110px;">Archery
<br />Ballooning<br />Biking</div>
<div style="float:left;width:50%;min-width:110px;">Bungee Jumping
<br />Coasteering<br />Dancing</div>
</div>
<div style="float:left;width:50%;min-width:110px;">
<div style="float:left;width:50%;min-width:110px;">Driving
<br />Gliding<br />Motor Racing</div>
<div style="float:left;width:50%;min-width:110px;">Murder Mystery
<br />Off-Roading<br />Paintballing</div>
</div>
这给了我们如下的 DOM 结构:
随着可用 width
的减小,百分比 width
s 导致每个列收缩,直到列达到由 min-width
属性设置的下限 width
。最先达到此阈值的 div
s 是内部 div
s(红色),因此当我们越过一个临界阈值时,外部 div
s(蓝色)变得足够窄,以至于它们无法在不换行的情况下容纳内部 div
s,所以我们看到如下效果:
正如你所见,垂直排序得到了保留。
这可以扩展到任何数量的初始列,只要它是 2 的幂,例如 4、8、16 或 32 列,这分别需要 2、3、4 和 5 个嵌套级别,其中每对 50% 宽度的浮动 div
s 都被一个超级的 50% 宽度的浮动 div
包含。
使用 8 列的此方法实际应用的示例可在此处找到 (作者已删除死站链接)。
服务器端要求
本文的主要目的是演示上述用于构建适应可用空间的“技巧”。为了说明服务器端要求,我们包含了一个 VB.NET 示例方法,该方法为具有 8 列的列表执行所需渲染。
Public Function HTMLMultiColumnList(ByVal listitems As List(Of String), _
ByVal widthpixels As Integer) As String
Dim output As New StringBuilder
'
' Calculate the number of items required in each block, and the number of blocks
' that will have an extra item to cope with the remainder.
'
Dim itemsperblock As Integer = CInt(Math.Floor((listitems.Count) / 8))
Dim blockswithextraitems As Integer = listitems.Count - (itemsperblock * 8)
'
' Build the sublists
'
Dim sublists(8) As List(Of String)
Dim counter As Integer = 0
For i As Integer = 0 To 8 - 1
sublists(i) = New List(Of String)
Dim itemsthisblock As Integer = itemsperblock
If i < blockswithextraitems Then itemsthisblock += 1
For k As Integer = 0 To itemsthisblock - 1
If counter < listitems.Count Then
sublists(i).Add(listitems(counter))
counter += 1
End If
Next
Next
'
' Render the sublists with nested float divs
'
Dim divopen As String = String.Format( _
"<div style=""float:left;width:50%;min-width:{0}px;"">",
widthpixels.ToString)
Dim divclose As String = "</div>"
For i As Integer = 0 To 7
If i Mod 4 = 0 Then output.Append(divopen)
If i Mod 2 = 0 Then output.Append(divopen)
output.Append(divopen)
output.Append(Join(sublists(i).ToArray, "<br />"))
output.Append(divclose)
If i Mod 2 = 1 Then output.Append(divclose)
If i Mod 4 = 3 Then output.Append(divclose)
Next
Return output.ToString
End Function
关注点
此方法的一个问题是换行使用模 2 进行,即 8 列列表换行到 4 列,然后是 2 列,然后是单列。然而,在每个阶段,包含列表项的底层 div
s 使用的宽度始终正好等于或大于可用宽度的 50%。虽然希望有一个能将 8 列换行到 7 列等的解决方案,但从视觉角度来看,这种方法效果相当好。
注意:请注意,上图中所示的边距和彩色边框不应使用。换行的 div
s 必须没有边框或边距,否则 50% 的宽度值将不再正确。如果你想“装饰”列表项块,则应使用最内层浮动 div
s 中的进一步标记,但是任何水平装饰都会在很大程度上削弱分开的块换行到彼此下方以产生连续垂直列表效果的目的。