修改
我接受了以下@ user943702给出的答案。我需要稍微修改它以使用我的Vue实现,如下面的代码段所示。
const theElements = [{
name: "ele1",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}, {
name: 4
}, {
name: 5
}]
}, {
name: "ele2",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele3",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele4",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele5",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele6",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele7",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele8",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele9",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele10",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele11",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}, {
name: 4
}, {
name: 5
}]
}, {
name: "ele12",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}];
new Vue({
el: '#ele-grid',
data: {
elements: theElements
},
methods: {
// find the first grid line excess {max}
// return index; -1 means no overflow
firstoverflowline: function(cols, max) {
var sum = 0;
for (var i = 0; i<cols.length; ++i) {
sum += cols[i];
if (sum >= max)
return i;
}
return -1;
},
// compute max no of columns in grid
// use by `grid-template-columns:repeat(<max>, max-content)`
computegridlines: function(container) {
var cols = getComputedStyle(container).gridTemplateColumns.split(/\s+/).map(parseFloat);
var x = this.firstoverflowline(cols, parseFloat(getComputedStyle(container).width));
if (x == -1) return;
container.style.gridTemplateColumns = `repeat(${x}, max-content)`;
this.computegridlines(container);
},
// polyfill `width:max-content`
maxcontent: function(container) {
var items = Array.from(container.children);
for(var i = 0; i < items.length; i++) {
var item = items[i];
item.style.display = "flex";
item.style.flexFlow = "column";
item.style.alignItems = "start";
var max = Array.from(item.children).reduce(function(max,item) {
var {left, right} = item.getBoundingClientRect();
return Math.max(max, right - left);
}, 0);
item.style.width = `${max}px`;
}
},
// flex-grid-ify a container
flexgrid: function(container) {
container.style.display = `grid`;
container.style.gridTemplateColumns = `repeat(${container.children.length}, max-content)`;
this.computegridlines(container);
this.maxcontent(container);
}
},
mounted: function() {
var container = document.getElementById('ele-grid');
var _this = this;
this.flexgrid(container);
window.onresize = function(e) { _this.flexgrid(container); }
}
});
#ele-grid {
width:100vw;
}
.ele-card {
border: 1px solid black;
background: cyan;
margin: 5px 3px;
}
.ele-card .children {
display: flex;
flex-wrap: nowrap;
padding: 5px;
}
.ele-card .child {
margin: 0 5px;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
border: 1px solid black;
background: magenta;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.min.js"></script>
<div id="ele-grid">
<div class="ele-card" v-for="ele in elements" :key="ele.name">
<div class="element">{{ele.name}}</div>
<div class="children">
<div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
</div>
</div>
</div>
我有不同数量的元素可以有不同的宽度。我想在网格中对齐这些元素,使其左侧在每列中对齐。此外,我希望在窗口尺寸较小并保持网格时包装元素。我在下面的图片中嘲笑了我想要的东西。
我正在使用VueJS 2将元素填充到网格和CSS Flexbox中,以使用以下CSS组织元素。下面是它现在如何运作的示例片段:
const theElements = [{
name: "ele1",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}, {
name: 4
}, {
name: 5
}]
}, {
name: "ele2",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele3",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele4",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele5",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele6",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele7",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele8",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele9",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele10",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}, {
name: "ele11",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}, {
name: 4
}, {
name: 5
}]
}, {
name: "ele12",
children: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}];
new Vue({
el: '#ele-grid',
data: {
elements: theElements
}
});
#ele-grid {
display: flex;
flex-wrap: wrap;
}
.ele-card {
border: 1px solid black;
background: cyan;
margin: 5px 3px;
}
.ele-card .children {
display: flex;
flex-wrap: nowrap;
padding: 5px;
}
.ele-card .child {
margin: 0 5px;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
border: 1px solid black;
background: magenta;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.min.js"></script>
<div id="ele-grid">
<div class="ele-card" v-for="ele in elements" :key="ele.name">
<div class="element">{{ele.name}}</div>
<div class="children">
<div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
</div>
</div>
</div>
这几乎有效;每个元素都有自己的宽度,并在调整窗口大小时进行换行。但是,元素不与网格对齐。
我也研究过使用CSS Grid,但看起来你必须要指定每个元素的宽度或列数,这两者都需要是任意的。
我对使用CSS或JavaScript的任何解决方案持开放态度(请不要使用JQuery)。我宁愿不包括第三方库,但如果它是唯一的选择,我会考虑它。
答案 0 :(得分:6)
修改:
max-content
属性来删除每列中的无关空格(不要将此属性与解释中的属性混淆,尽管这是每个元素的宽度值,这个是每列的基础)对于空间分布:有一个名为justify-content
的便捷属性我选择将其设置为居中,除了其他值之外,您可以将其设置为:
space-between; /* The first item is flush with the start,the last is flush with the end */
space-around; /* Items have a half-size space on either end */
space-evenly; /* Items have equal space around them */
stretch; /* Stretch 'auto'-sized items to fitthe container */
在进入剧本之前,有几点注意事项:
@media query
每个宽度一个,然后使用它完成,但是单个元素也有任意宽度,所以我将使用JavaScript 编辑:这是一个使用CSS media query
方法通知的脚本,您尝试将其自定义为不同的设备宽度越多,当单个元素宽度意外更改时,您抓到的风险就越大
const theElements = [{ name: "ele1", children: [{ name: 1 }, { name: 2 }, { name: 3 }, { name: 4 }, { name: 5 }]}, { name: "ele2", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele3", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele4", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele5", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele6", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele7", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele8", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele9", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele10", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele11", children: [{ name: 1 }, { name: 2 }, { name: 3 }, { name: 4 }, { name: 5 }]}, { name: "ele12", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}];
new Vue({
el: '#ele-grid',
data: {
elements: theElements
}
});
@media (min-width: 1020px) {
#ele-grid {
display:grid;
grid-template-columns:repeat(5, 1fr);
justify-content: center;
}
}
@media (min-width:400px) and (max-width: 1020px) {
#ele-grid {
display:grid;
grid-template-columns:max-content max-content max-content;
}
}
@media (max-width: 400px) {
#ele-grid {
display:grid;
grid-template-columns:max-content;
}
}
.ele-card {
margin: 5px 3px;
}
.ele-card .children {
display: flex;
flex-wrap: nowrap;
padding: 5px;
}
.ele-card .child {
margin: 0 5px;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
border: 1px solid black;
background: magenta;
}
.wrapper{
border: 1px solid black;
background: cyan;
display:inline-block;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>
<div id="ele-grid">
<div class="ele-card" v-for="ele in elements" :key="ele.name">
<div class="wrapper">
<div class="element">{{ele.name}}</div>
<div class="children">
<div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
</div>
</div>
</div>
</div>
display:inline-block
预期行为CSS grid layout
,您可以使用css列或垂直flex
代替,但元素将从上到下对齐,从而更改整个布局。这是针对css的JavaScript:
const theElements = [{ name: "ele1", children: [{ name: 1 }, { name: 2 }, { name: 3 }, { name: 4 }, { name: 5 }]}, { name: "ele2", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele3", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele4", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele5", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele6", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele7", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele8", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele9", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele10", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}, { name: "ele11", children: [{ name: 1 }, { name: 2 }, { name: 3 }, { name: 4 }, { name: 5 }]}, { name: "ele12", children: [{ name: 1 }, { name: 2 }, { name: 3 }]}];
new Vue({
el: '#ele-grid',
data: {
elements: theElements
}
});
function resizeHandler(){
colStart=10; // max number of columns to start with
allCards= document.getElementsByClassName('wrapper');
totalWidth=0;
maxWidTab=[];
for (i=colStart;i>0;i--){
for(j=0;j<i; j++){ //initializing and resetting
maxWidTab[j]=0;
}
for (j=0; j<allCards.length; j++){
cellWidth=parseInt(getComputedStyle(allCards[j]).width); //parseInt to remove the tailing px
maxWidTab[j%i]<cellWidth?maxWidTab[j%i]=cellWidth:'nothing to be done';
}
for(j=0;j<i; j++){ //sum to see if fit
totalWidth+=maxWidTab[j]+2+6 //borders and margins
}
if (totalWidth<innerWidth){
grEl = document.getElementById("ele-grid");
grEl.style.gridTemplateColumns="repeat("+i+", max-content)";
/*console.log(i);*/
break;
}else{
totalWidth=0; //resetting
}
}
}
window.addEventListener("resize",resizeHandler);
document.addEventListener ("DOMContentLoaded",resizeHandler);
#ele-grid {
display:grid;
justify-content: center;
grid-template-columns:repeat(10, max-content); /* starting by 10 columns*/
}
.ele-card {
margin: 5px 3px;
}
.ele-card .children {
display: flex;
flex-wrap: nowrap;
padding: 5px;
}
.ele-card .child {
margin: 0 5px;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
border: 1px solid black;
background: magenta;
}
.wrapper{
border: 1px solid black;
background: cyan;
display:inline-block;
}
</style>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>
<div id="ele-grid">
<div class="ele-card" v-for="ele in elements" :key="ele.name">
<div class="wrapper">
<div class="element">{{ele.name}}</div>
<div class="children">
<div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
</div>
</div>
</div>
</div>
答案 1 :(得分:2)
CSS grid-template-columns
确实支持内容感知值max-content
。唯一的问题是应该有多少列。
我编写了一个算法来探测最大列数。实现涉及JS并要求浏览器支持CSS Grid。可以找到演示here。 (我使用Pug创建与您相同的源结构,样式也与您的相同,以便我们可以专注于JS面板,实现)。
在演示中,更改视口大小将重新生成网格项。您可以通过调用flexgrid(container)
手动触发其他有趣时刻的重新流动,例如异步加载项目然后重新流动。只要源结构保持不变,就允许更改项的维度属性。
这是算法
Step1)将容器设置为网格格式上下文,将所有网格项布局在一行中,将每列宽度设置为max-content
|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggg|hhh|iii|
Step2)找到第一个溢出网格线
|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggg|hhh|iii|
^overflowed
Step3)在我们的例子中将grid-template-columns
减少到3。
由于grid-row
默认为auto
,因此CSS引擎会布局网格项
在超出最后一列网格线的下一行。我叫了这个
“包装”。此外,网格项目由于grid-template-columns:max-content
自动扩展(例如“ddd”扩展到第一列最宽内容的长度)
|---container---|
|aaaaa|bbb|ccc|
|ddd |eee|fff|
|ggggg|hhh|iii|
由于所有列网格线都位于“内部”容器中,我们已经完成了。在某些情况下,在“包装”之后引入新的溢出网格线,我们需要重复步骤2和3,直到所有网格线都位于“内部”容器中,例如,
#layout in one row
|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggggg|hhhhh|iii|
#find the first overflowed grid line
|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggggg|hhhhh|iii|
^overflowed
#reduce `grid-template-columns`
|---container---|
|aaaaa |bbb |ccc|
|ddd |eee |fff|
|ggggggg|hhhhh|iii|
#find the first overflowed grid line
|---container---|
|aaaaa |bbb |ccc|
|ddd |eee |fff|
|ggggggg|hhhhh|iii|
^overflowed
#reduce `grid-template-columns`
|---container---|
|aaaaa |bbb |
|ccc |ddd |
|eee |fff |
|ggggggg|hhhhh|
|iii |
#find the first overflowed grid line
#None, done.