纯CSS解决方案,用于将项目拆分为动态数量的列

Cos*_*ert 41 html css css3 flexbox

有没有办法在多列中对齐项目,列数取决于最宽的项目?项目高度和容器宽度都是固定的,但项目宽度是动态的.

我正在寻找一种只有CSS的方法来实现以下行为:

(假设父容器的宽度为300px.)

  • 如果最宽的项目大于150px,请使用单个列
  • 如果最宽的项目介于100px和150px之间,请使用两列
  • 如果最宽的项目小于100px,请使用三列
  • ...
  • 如果最宽的项目小于容器宽度/ N,请使用N列

获得此行为的一种可能方法是使用JavaScript并使用JavaScript display:inline-blockwidth属性设置为容器中最宽元素的宽度.

请参阅此JSFiddle以获取示例:

例

但是,我认为应该只有CSS这样做.可能吗?

如果没有,也许有一种优雅的CSS方式将动态大小的项目分发/捕捉到具有固定宽度的容器中的列?

Abh*_*lks 25

...我正在寻找一种只有CSS的方法来实现以下行为......如果最宽的项目比...更宽

......我认为应该只有一种CSS的方式来做到这一点......

如@ Paulie-D所示,CSS无法检测到您孩子div的宽度变化,因此不存在纯CSS唯一解决方案.

这是因为您希望获得所有元素的宽度,然后获取其中的最大值,然后使用该宽度将元素分配到列中.这个计算超出了CSS.你需要Javascript才能做到这一点.

如果没有,也许有一种优雅的CSS方式将动态大小的项目分发/捕捉到具有固定宽度的容器中的列?

我将分两部分解释:


第1部分,CSS:

当我们说我们希望内容在列中时,它意味着从上到下的流而不是从左到右的包装流.为此我们需要CSS Columns.

诀窍是指定autocolumn-count / column-width.这将自动将内容分配到父宽度内所需的列数.

我在上面的陈述中犯了一个根本性的错误(因此另一个编辑).根据这里规范,算法说:

(01) if ((column-width = auto) and (column-count = auto)) then
(02)      exit; /* not a multicol element */ 

这是我之前错的地方.当两个column-countcolumn-width被设置为auto那么它被视为不是MULTICOL元件.当其中一个属性设置为非自动值时,则另一个属性由此确定.

上面的参考:

如果column-count设置为auto,则列数将由其他属性确定(例如,column-width如果它具有非自动值)并且如果column-width设置为auto,则列宽将由其他属性确定(例如column-count,如果它有一个非自动值)

一个例子是设置column-width为固定宽度,比如说120px(稍后我们将在第2部分中处理):

.container { -webkit-columns: auto 120px; columns: auto 120px; }
Run Code Online (Sandbox Code Playgroud)

这将使容器在尽可能多的列中匹配内容,以使列宽度120px在其自己的宽度内.如果增加容器宽度,它将获得更多列.如果减小容器宽度,当没有足够的可用空间时,它将获得更少的列,最终会折叠到单个列.

请参阅下面的代码段中的完整示例:

例1:

* { box-sizing: border-box; padding: 0; margin: 0; }
p { margin-left: 16px; }
.container { width: 400px; border: 1px solid #f00; margin: 16px; }
.container.col { -webkit-columns: auto 120px; columns: auto 120px; }
.container > div { 
	-webkit-column-break-inside: avoid; column-break-inside: avoid; 
	display: block; padding: 8px; border: 1px solid #ccc;
}
#one { width: 200px; }
#two { width: 300px; }
Run Code Online (Sandbox Code Playgroud)
<p>Small Container (1-column):</p>
<div id="one" class="container col">
	<div class="item-min">Maudie Mcmanus</div>
	<div class="item-min">Remedios</div>
	<div class="item-min">Chaya B</div>
	<div class="item-min">Duncan</div>
	<div class="item-min">Lashonda</div>
</div>
<p>Medium Container (2-column):</p>
<div id="two" class="container col">
	<div class="item-min">Maudie Mcmanus</div>
	<div class="item-min">Remedios</div>
	<div class="item-min">Chaya B</div>
	<div class="item-min">Duncan</div>
	<div class="item-min">Lashonda</div>
</div>
<p>Large Container (3-column):</p>
<div id="three" class="container col">
	<div class="item-min">Maudie Mcmanus</div>
	<div class="item-min">Remedios</div>
	<div class="item-min">Chaya B</div>
	<div class="item-min">Duncan</div>
	<div class="item-min">Lashonda</div>
</div>
Run Code Online (Sandbox Code Playgroud)

小提琴1:http://jsfiddle.net/abhitalks/tgwp4b7a/2/show

在上面的代码片段中,我们column-count: auto在容器上使用,并且是任意的column-width: 120px(仅用于演示).这就是它的全部.上面的代码中有三个例子:(1)容器宽度小,内容分布在一列,因为它们受到column-width; (2)容器中等宽度,内容分为两列,因为现在有更多可用空间; (3)容器宽度大得多,可容纳三根柱子.

作为副作用,如果容器的宽度以百分比表示,那么整个装置也会自动响应.在较大的屏幕上显示更多列,并在较小的屏幕上折叠到一列.

但是,这取决于您为容器提供的固定宽度,column-width因此也可以称为幻数解决方案.但是,这不是我们想要的.我们不希望根据容器的宽度确定列,我们希望列由内容宽度确定.我们将在后面的第2部分中看到如何消除这种依赖性.


第2部分,使用Javascript扩展它:

既然我们已经确定了元素可以由CSS自动分布在列中,具体取决于父级可用的宽度,我们可以扩展它以消除我们通过Javascript对固定宽度的依赖.

回到你的问题:

......如果最宽的项目比...更宽

为了确定最宽的项目并将宽度应用于其余项目,您需要的只是一个众所周知的双线Javascript:

var maxWidth = Math.max.apply(null, $("div.item").map(function () {
    return $(this).width();
}).get());
Run Code Online (Sandbox Code Playgroud)

我们还设置子项divinline-block防止包装以识别实际宽度.所以,你必须添加到我们在第1部分中编写的CSS中的是:

.container > div {
    display: inline-block; 
    white-space: nowrap;   /* prevents wrapping and helps getting actual width */
}
Run Code Online (Sandbox Code Playgroud)

然后我们需要做两件事:(1)将column-widthon容器设置为我们上面计算的最大宽度; (2)将这个宽度设置给所有孩子div,以便他们整齐地堆叠.此外,我们不需要column-count / column-width在CSS中设置,因为我们必须在Javascript中这样做.

$("#container").css({ "column-width": maxWidth }).find('div').width(maxWidth);
Run Code Online (Sandbox Code Playgroud)

请参阅下面的代码段中的完整示例:

例2:

//large
	var maxWidth = Math.max.apply(null, $("#one > div").map(function () { return $(this).outerWidth(); }).get());
	$("#one").css({ "-webkit-column-width": maxWidth, "column-width": maxWidth }).find('div').outerWidth(maxWidth);

	// medium
	var maxWidth2 = Math.max.apply(null, $("#two > div").map(function () { return $(this).outerWidth(); }).get());
	$("#two").css({ "-webkit-column-width": maxWidth2, "column-width": maxWidth2 }).find('div').outerWidth(maxWidth2);

	// small
	var maxWidth3 = Math.max.apply(null, $("#three > div").map(function () { return $(this).outerWidth(); }).get());
	$("#three").css({"-webkit-column-width": maxWidth3, "column-width": maxWidth3 }).find('div').outerWidth(maxWidth3);
Run Code Online (Sandbox Code Playgroud)
* { box-sizing: border-box; padding: 0; margin: 0; }
	p { margin-left: 16px; }
	.container { width: 450px; border: 1px solid #f00; margin: 16px; }
	.container > div { 
		-webkit-column-break-inside: avoid; column-break-inside: avoid; 
		display: inline-block;  white-space: nowrap;
		padding: 8px; border: 1px solid #ccc;
	}
Run Code Online (Sandbox Code Playgroud)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
	<p>Normal children (wrapping):</p>
	<div class="container">
		<div class="item">Maudie Mcmanus</div>
		<div class="item">Remedios Arrington</div>
		<div class="item">Chaya B</div>
		<div class="item">Duncan</div>
		<div class="item">Lashonda Tatum Walls</div>
	</div>
	<p>Large children (1-column):</p>
	<div id="one" class="container">
		<div class="item-min">Maudie Mcmanus Mcmanus Mcmanus</div>
		<div class="item-min">Remedios</div>
		<div class="item-min">Chaya B</div>
		<div class="item-min">Duncan</div>
		<div class="item-min">Lashonda</div>
	</div>
	<p>Medium children (2-column):</p>
	<div id="two" class="container">
		<div class="item-min">Maudie Mcmanus Mcmanus</div>
		<div class="item-min">Remedios</div>
		<div class="item-min">Chaya B</div>
		<div class="item-min">Duncan</div>
		<div class="item-min">Lashonda</div>
	</div>
	<p>Small children (3-column):</p>
	<div id="three" class="container">
		<div class="item-min">Maudie Mcmanus</div>
		<div class="item-min">Remedios</div>
		<div class="item-min">Chaya B</div>
		<div class="item-min">Duncan</div>
		<div class="item-min">Lashonda</div>
	</div>
Run Code Online (Sandbox Code Playgroud)

小提琴2:http://jsfiddle.net/abhitalks/ojd57678/4/show

(更改了上面的代码片段和小提琴.感谢@Yandy_Viera,他指出.outerWdith应该使用jQuery 而不是.width(忽略box-sizing: border-box,导致设置不正确的宽度).

在上面的代码片段中,我们现在使用三个示例变体:(1)其中子divs的宽度较大,并且由于它们受到容器宽度的约束而分布在一列中; (2)儿童div的宽度较小,分布在两列,因为现在有更多的空间; (3)儿童div的宽度非常小,可容纳三列.


如您所见,CSS可以帮助根据可用宽度将内容分配到列中,但无法计算并将最宽的元素宽度应用于每个元素宽度.对于这个,双线Javascript可以让你完成你最初想要的.

注意:当我第一次阅读您的问题时,我的印象是您已经拥有了一个有效的Javascript解决方案,而且我不确定您是否想要一个.然而,在第二次阅读时,我意识到你没有,并且Javascript角度对于理解是必不可少的.因此,这个编辑添加了一个Javascript部分.

注2:这个答案的前一个版本存在一个缺陷,我在列属性的自动值中出现了一个基本错误.这需要另一个编辑.


Yan*_*era 10

他们在评论中所说的一切,都不存在一个只有CSS的解决方案,所以我想给你一个很好的解决方案js,你甚至不必迭代寻找最宽的项目,一切都来自一组CSS和数学.

您可以更改元素的内容,您将看到如何调整列数,如果最宽项小于容器宽度/ N,它将自动使用N列.

看看这个JSFiddle进行演示

这个想法是很简单的,你set width: 'auto',display: 'inline-block'container允许它适合它是由最宽的元素的宽度限定其内容看到它在波纹管IMG:

在此输入图像描述

现在你可以使用宽度container来知道最宽项目的宽度,这可以避免你必须迭代寻找它.现在你可以使用这个宽度来设置剩下的项目,但是让我们做得更好一点,我们可以让元素占用整个宽度,container只需要一个litter calc:

var div = Math.floor(initialWidth / widestItemWidth);     /*this is to get as many items fit with the width of the widest element*/
var itemWidth = initialWidth / div;      /*for the width of the item occupy the entire container*/
Run Code Online (Sandbox Code Playgroud)

因此,如果最宽的项目小于容器宽度/ N,它将自动使用N列,它们将适合宽度container,这就是你要做的全部,只需设置一个css属性,做一点计算,你有什么你想实现.

$(document).ready(function() {
    var $container = $('.container');
    var $item = $('.item');

    var initialWidth = $container.outerWidth();

   $container.css({     /*to allow it to fit its contents which is defined by the width of the widest element*/
        width: 'auto',
        display: 'inline-block'
    });   

    var widestItemWidth = $container.outerWidth();     /*Now this is the width of the widest item*/

    var div = Math.floor(initialWidth / widestItemWidth);     /*this is to get as many items fit with the width of the widest element*/
    var itemWidth = initialWidth / div;      /*for the width of the item occupy the entire container*/
  
  /*if you don't want the items occupy the entire width of the container just use 'widestItemWidth' as width of $item*/

    $item.css({
        width: itemWidth,
        float: 'left'
    });
    $container.css({     /*restoring the initial styles*/
        width: initialWidth,
        display: 'block'
    });
})
Run Code Online (Sandbox Code Playgroud)
.container {
  width: 400px;
  overflow: hidden;
}
.container .item {
  padding: 8px;
  border: 1px solid rgb(216, 213, 213);
}
*{
  box-sizing: border-box;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
    <div class="item">Maudie Mcmanus</div>
    <div class="item">Remedios Arrington</div>
    <div class="item">Chaya B</div>
    <div class="item">Duncan</div>
    <div class="item">Lashonda Tatum Walls</div>
</div>
Run Code Online (Sandbox Code Playgroud)

如果你想为你的物品添加保证金,你只需要在做这样的数学时考虑它:

$(document).ready(function() {
    var $container = $('.container');
    var $item = $('.item');

    var initialWidth = $container.outerWidth();

    $container.css({
            width: 'auto',
            display: 'inline-block'
        });

    var currentWidth = $container.outerWidth();
    var itemMargin= parseInt($item.css('margin-right'));

    var div = Math.floor(initialWidth / currentWidth);
    var itemWidth = initialWidth / div - itemMargin;   /*subtracting the item's margin from the compute width*/

    $item.css({
        width: itemWidth,
        float: 'left'
    });
    $container.css({
        width: initialWidth,
        display: 'block'
    });
})
Run Code Online (Sandbox Code Playgroud)
.container {
  width: 400px;
  overflow: hidden;
}
.container .item {
  padding: 8px;
  border: 1px solid rgb(216, 213, 213);
  margin-right: 3px;
}
*{
  box-sizing: border-box;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
    <div class="item">Maudie Mcmanus</div>
    <div class="item">Remedios Arrington</div>
    <div class="item">Chaya B</div>
    <div class="item">Duncan</div>
    <div class="item">Lashonda Tatum Walls</div>
</div>
Run Code Online (Sandbox Code Playgroud)

这里有一个JSFiddle示例