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

时间:2015-08-17 11:23:21

标签: html css css3 flexbox

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

我正在寻找一种仅支持CSS的方法来实现以下行为:

  

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

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

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

有关示例,请参阅this JSFiddle

Example

但是,我认为应该只有CSS这样做的方法。有可能吗?

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

6 个答案:

答案 0 :(得分:25)

  

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

     

......我认为应该只有CSS的方式   这样做......

As indicated by @Paulie-D,CSS无法检测到您的孩子div中的宽度变化,因此不存在纯CSS唯一解决方案。

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

  

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

我将分两部分解释:

第1部分,CSS:

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

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

我在上面的陈述中犯了一个根本性的错误(因此另一个编辑)。根据{{​​3}}算法说:

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

这是我之前错的地方。当column-countcolumn-width都设置为auto时,它将被视为而不是多元素。当其中一个属性设置为非自动值时,则另一个属性由此确定。

来自specs here

  

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

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

.container { -webkit-columns: auto 120px; columns: auto 120px; }

这将使容器适合尽可能多的列中的内容,因为它的宽度为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; }

<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>
&#13;
&#13;
&#13;

小提琴1:above ref

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

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

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

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

现在我们已经确定了CSS可以根据父级可用的宽度自动分配元素,我们可以扩展它以消除我们通过Javascript对固定宽度的依赖。

回到你的问题:

  

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

为了确定最宽的项目并将该宽度应用于其余项目,您需要的只是http://jsfiddle.net/abhitalks/tgwp4b7a/2/show双线Javascript:

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

我们还将子div设置为inline-block,以防止换行以识别实际宽度。所以,你必须添加到我们在第1部分中编写的CSS中就是:

.container > div {
    display: inline-block; 
    white-space: nowrap;   /* prevents wrapping and helps getting actual width */
}

然后我们需要做两件事:(1)将容器上的column-width设置为我们上面计算的最大宽度; (2)将此宽度设置为所有子div,以允许它们整齐地堆叠。此外,我们不需要在CSS中设置column-count / column-width,因为我们必须在Javascript中执行此操作。

$("#container").css({ "column-width": maxWidth }).find('div').width(maxWidth);

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

示例2

&#13;
&#13;
//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);
&#13;
* { 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;
	}
&#13;
<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>
&#13;
&#13;
&#13;

小提琴2:a well-known

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

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

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

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

注意2:此答案的上一版本存在缺陷,我在列属性的自动值中存在一个基本错误。这需要进行另一次编辑。

答案 1 :(得分:10)

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

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

Check out this JSFiddle for demo

这个想法非常简单,你set width: 'auto'display: 'inline-block'container允许它适合其内容,这个内容由最宽元素的宽度定义,请参阅下面的图片img :

enter image description here

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

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

因此,如果最宽的项目小于容器宽度/ 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'
    });
})
.container {
  width: 400px;
  overflow: hidden;
}
.container .item {
  padding: 8px;
  border: 1px solid rgb(216, 213, 213);
}
*{
  box-sizing: border-box;
}
<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>

如果您想为商品添加保证金,那么在进行数学计算时必须考虑到这一点:

$(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'
    });
})
.container {
  width: 400px;
  overflow: hidden;
}
.container .item {
  padding: 8px;
  border: 1px solid rgb(216, 213, 213);
  margin-right: 3px;
}
*{
  box-sizing: border-box;
}
<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>

Here a JSFiddle example to play with

答案 2 :(得分:4)

使用纯CSS解决方案可能获得的最接近的是使用其他人(或CSS网格方法 - 请参阅编辑)建议的CSS列。但是,关键是使用相对单位并非常了解布局中列位容器的位置和位置,并使用媒体查询来控制列数。这绝对是 NOT 一个防弹解决方案。但是,如果要创建布局并控制列容器元素的放置和范围,则可以相当可靠地控制列数。如果这是针对您无法控制的窗口小部件,您确实需要实现基于Javascript的解决方案。

参考链接:Responsive CSS ColumnsCSS Grid Layout

编辑:在再次阅读您的问题并查看您的屏幕截图示例后,看起来您实际上正在寻找一种常见的CSS网格网格解决方案,其中元素水平流动而不是垂直流动。我更新了我的答案,包括一个片段演示,其中块重新流动。

基于列的媒体查询和容器宽度的片段演示(大约最小列宽为10em):

#main, #sideBar {
    box-sizing:border-box;
    display:block;
    padding:0;
    margin:0;
    width:100%;
}

.container {
    width: 100%;
    min-width:10em;
    -webkit-columns: 1;
    -moz-columns: 1;
      columns: 1;
}
.container .item {
    display: block;
    padding: 8px;
    border: 1px solid rgb(216, 213, 213);
    box-sizing:border-box;
    -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
          page-break-inside: avoid; /* Firefox */
               break-inside: avoid; /* IE 10+ */
}
@media only screen and (min-width:20em) {
    .container {
    -webkit-columns: 2;
    -moz-columns: 2;
      columns: 2;
    }
}

@media only screen and (min-width:32em) {
    #main {
        float:left;
        width:65%;
    }

    #sideBar {
        float:left;
        width:30%;
        margin-left:5%;
    }
    #sideBar .container {
    -webkit-columns: 1;
    -moz-columns: 1;
      columns: 1;
    }

}

@media only screen and (min-width:48em) {
    
    #main .container {
    -webkit-columns: 3;
    -moz-columns: 3;
      columns: 3;
    }

    #sideBar .container {
    -webkit-columns: 1;
    -moz-columns: 1;
      columns: 1;
    }

}

@media only screen and (min-width:52em) {
    
    #sideBar .container {
    -webkit-columns: 2;
    -moz-columns: 2;
      columns: 2;
    }

}

@media only screen and (min-width:100em) {
    
    #main .container {
    -webkit-columns: 5;
    -moz-columns: 5;
      columns: 5;
    }

    #sideBar .container {
    -webkit-columns: 3;
    -moz-columns: 3;
      columns: 3;
    }

}
<div id="main"><h1>#main</h1>
    <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>
</div>
<div id="sideBar"><h1>#sideBar</h1>
    <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>
</div>

编辑:使用CSS网格方法添加了此片段演示,该方法可以水平而不是垂直地重新流动块。

#main, #sideBar {
    box-sizing:border-box;
    display:block;
    padding:0;
    margin:0;
    width:100%;
    clear:left;
}

.container {
    width: 100%;
    min-width:10em;
}

.container .item {
    display: block;
    float:left;
    padding: .5em;
    border: 1px solid rgb(216, 213, 213);
    box-sizing:border-box;
    vertical-align: top;

}

@media only screen and (min-width:20em) {
    .container .item {
        margin: 0 1%;
        width:48%;
    }
}

@media only screen and (min-width:32em) {
    #main {
        float:left;
        width:65%;
    }

    #sideBar {
        float:left;
        clear:none;
        width:30%;
        margin-left:5%;
    }
    

    #sideBar .container .item {
        margin: 0;
        width:100%;
    }

}

@media only screen and (min-width:48em) {
    
    #main .container .item {
        width:31%;
    }

}

@media only screen and (min-width:52em) {
    
    #sideBar .container .item {
        margin: 0 1%;
        width:48%;
    }


}

@media only screen and (min-width:100em) {
    
    #main .container .item {
        width:18%;
    }

    #sideBar .container .item {
        width:31%;
    }

}
<div id="main"><h1>#main</h1>
    <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>
</div>
<div id="sideBar"><h1>#sideBar</h1>
    <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>
</div>

答案 3 :(得分:1)

首先 - 看看 Fiddle Demo (使用.container的宽度播放...)

这不可能只使用CSS,但这是一个使用jquery的优雅解决方案,它检测容器的宽度,最宽的项目宽度,并将项目分散到相关的列数(动态创建)。

<强> HTML

<p>Without width:
    <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>

<强>的Javascript

$(function(){
    var $items = $('.item');

    // detect the widest column's width
    var widestItemWidth = 0;
    $items.each(function(){
        widestItemWidth = Math.max(widestItemWidth, $(this).width());
    });

    // calculate the number of columns
    var $container = $('.container');
    var containerWidth = $container.width();
    var numOfColumns = parseInt(containerWidth / widestItemWidth);

    // create the columns
    for(var i = 0; i < numOfColumns; i++){
        $container.prepend('<div class="column"></div>');
    }
    var $columns = $('.column');
    $columns.css('width', (100/numOfColumns)+'%');

    // spread the items between the columns
    $items.each(function(i){
        var columnIndex = i % numOfColumns;
        $(this).appendTo($columns.eq(columnIndex));
    });
});

<强> CSS

.container {
    width: 400px;
}
.container .column {
    display: inline-block;
    box-sizing: border-box;
    vertical-align: text-top;
}
.container .item {
    display: inline-block;
    padding: 8px;
    border: 1px solid rgb(216, 213, 213);
}

答案 4 :(得分:0)

最简单的似乎是将宽度设置为32%加上clear:所有列都没有,单独的clear:每三(最后)列都有一个类。添加媒体查询以解决不同的屏幕尺寸并相应地设置宽度,例如, 24%,48%等

答案 5 :(得分:0)

试试这个例子

@media only screen and (device-width : 300px) {#container {width:300px; display:block;}}
@media only screen and (device-width : 150px) {#container {width:150px; display:block; float:left}}
@media only screen and (device-width : 100px) {#container {width:150px; display:block; float:left}}