响应地将 2 列布局折叠为单列

Responsively collapse 2 column layout to single column

我目前有一个由内容框组成的页面,布局使得有两个宽度不等的浮动列,并且每个框的高度不同以适合其内容,如下所示:

.box {
  padding: 1em;
  border: 1px solid black;
  margin: 3px;
}

.left.col {
  float: left;
  width: 70%;
}

.right.col {
  float: right;
  width: 30%;
}

.b1 {
  background-color: red;
  height: 150px;
}

.b2 {
  background-color: green;
  height: 80px;
}

.b3 {
  background-color: blue;
  height: 80px;
}

.b4 {
  background-color: yellow;
  height: 40px;
}

.b5 {
  background-color: purple;
  height: 30px;
}

.b6 {
  background-color: cyan;
  height: 40px;
}

.b7 {
  background-color: orange;
  height: 40px;
}
<div class="boxes">
  <div class="left col">
    <div class="box b1">1</div>
    <div class="box b2">2</div>
    <div class="box b3">3</div>
  </div>
  <div class="right col">
    <div class="box b4">4</div>
    <div class="box b5">5</div>
    <div class="box b6">6</div>
    <div class="box b7">7</div>
  </div>
</div>

我希望能够响应式地将较小屏幕上的布局折叠成一个列,但这样我就能够控制内容框的顺序。 为此,我很乐意将 flexbox order 与适当的 polyfill 一起用于不支持的浏览器,例如 flexibility。 像这样:

/* @media screen and (max-width: 570px) */
.boxes {
  display: flex;
  flex-direction: column;
}

.box {
  padding: 1em;
  border: 1px solid black;
  margin: 3px;
}

.b1 {
  background-color: red;
  height: 150px;
  order: 1
}

.b2 {
  background-color: green;
  height: 80px;
  order: 3
}

.b3 {
  background-color: blue;
  height: 80px;
  order: 5
}

.b4 {
  background-color: yellow;
  height: 40px;
  order: 2;
}

.b5 {
  background-color: purple;
  height: 30px;
  order: 4
}

.b6 {
  background-color: cyan;
  height: 40px;
  order: 6
}

.b7 {
  background-color: orange;
  height: 40px;
  order: 7
}
<div class="boxes">
  <div class="box b1">1</div>
  <div class="box b2">2</div>
  <div class="box b3">3</div>
  <div class="box b4">4</div>
  <div class="box b5">5</div>
  <div class="box b6">6</div>
  <div class="box b7">7</div>
</div>

到目前为止我还没有做到的是将其作为一种单一的响应式方法来实现。我想我需要摆脱 <div class="col"> 以便使用 flex 控制框顺序,但似乎无法使用 flexbox 和不使用 float 实现相同的 2 列布局。

是否有一个单一的 (CSS) 解决方案可以实现两种布局并响应地切换?

编辑

IE9 和 Android 4.x 都占我过去一年当前观众的 2% 左右,所以我仍然需要支持他们。因此,任何使用现代 CSS 技术(例如 Flexbox、CSS 网格)的解决方案都需要使用 polyfill 或优雅地 fallback/degrade.

进行备份

如果你只能使用flexbox,那么布局很简单nested containers or a height or max-height on the container

此布局最有效的 CSS 解决方案是使用网格技术。

下面的解决方案是完整的

jsFiddle demo

.boxes {
  display: grid;
  grid-template-columns: 3fr 1fr;
  grid-auto-rows: 10px;
  grid-gap: 5px;
  grid-template-areas:
   " red yellow"
   " red yellow"
   " red yellow"
   " red yellow"
   " red yellow"
   " red yellow"   
   " red purple"
   " red purple"
   " red purple"
   " red purple"
   " red purple"   
   " red cyan"
   " red cyan"
   " red cyan"
   " red cyan"
   " green cyan"
   " green orange "
   " green orange "
   " green orange "
   " green orange "
   " green orange "   
   " green . " 
   " green . " 
   " green . " 
   " blue . " 
   " blue . "  
   " blue . "    
   " blue . "  
   " blue . "  
   " blue . "  
   " blue . "   
   " blue . "     
}

.b1 { grid-area: red;    background-color: red;    }
.b2 { grid-area: green;  background-color: green;  }
.b3 { grid-area: blue;   background-color: blue;   }
.b4 { grid-area: yellow; background-color: yellow; }
.b5 { grid-area: purple; background-color: purple; }
.b6 { grid-area: cyan;   background-color: cyan;   }
.b7 { grid-area: orange; background-color: orange; }

.box {
  padding: 1em;
  border: 1px solid black;
}

@media (max-width: 570px) {
  .boxes { grid-template-columns: 1fr;
           grid-template-areas: 
   " red "
   " red "
   " red "
   " red "
   " red "
   " red "
   " red "
   " red "
   " red "
   " red "
   " red "   
   " red "
   " red "
   " red "
   " red "
   " yellow "
   " yellow "
   " yellow "
   " yellow "
   " yellow "
   " yellow "
   " green "
   " green "
   " green "
   " green "
   " green "
   " green "   
   " green " 
   " green " 
   " green " 
   " purple "
   " purple "
   " purple "
   " purple "
   " purple "
   " blue " 
   " blue "  
   " blue "    
   " blue "  
   " blue "  
   " blue "  
   " blue "   
   " blue "     
   " cyan "
   " cyan "
   " cyan "
   " cyan "
   " cyan "
   " orange "
   " orange "
   " orange "
   " orange "
   " orange " ;}

}
<div class="boxes">
  <div class="box b1">1</div>
  <div class="box b2">2</div>
  <div class="box b3">3</div>
  <div class="box b4">4</div>
  <div class="box b5">5</div>
  <div class="box b6">6</div>
  <div class="box b7">7</div>
</div>

特点:

  • 现代CSS3技术
  • 一个有两列的容器(3fr1fr)(
  • 根据需要自动创建行;每行高 10 像素...
  • 所以跨越四行的网格区域是 40px(加上任何 grid-row-gap
  • 网格区域使用 grid-template-areas 属性
  • 中的字符串值进行布局
  • 可以使用其他方法(例如line-based placement)布置网格区域

嵌套 flexboxes 似乎可行,但我不确定在这个 SO 嵌入式 HTML 引擎中调试响应模式。似乎下面应该做的事情。基本上是 3 个 flexbox,其中外部 flexbox 在行和列之间切换/100% 宽度...

.boxes{
display:flex;

justify-content:center;
align-content:center;
align-items:center;
}

.col{
display:flex;
flex-flow: column nowrap;
justify-content:center;
align-content:center;
align-items:center;
}

.left{
flex: 0 1 auto;
min-width: 70%;
}
.right{
flex: 0 1 auto;
align-self:flex-start;
min-width: 30%;
}

.box{
flex: 0 1 auto;
min-width:100%
}


.b1{ background-color: red; height: 150px;}
.b2{ background-color: green; height: 80px;}
.b3{ background-color: blue; height: 80px;}
.b4{ background-color: yellow; height: 40px;}
.b5{ background-color: purple; height: 30px;}
.b6{ background-color: cyan; height: 40px;}
.b7{ background-color: orange; height: 40px;}

@media screen (max-width: 639px) {
.boxes{
flex-flow: column nowrap;
}
.left{
min-width: 100%;
}
.right{
min-width: 100%;
}
}
@media screen (min-width: 640px) {
.boxes{
flex-flow: row nowrap;
}
}
   
<div class="boxes">
<div class="left col">
    <div class="box b1">1</div>
    <div class="box b2">2</div>
    <div class="box b3">3</div>
</div>
<div class="right col">
    <div class="box b4">4</div>
    <div class="box b5">5</div>
    <div class="box b6">6</div>
    <div class="box b7">7</div>
</div>
</div>

因为唯一的纯 CSS 解决方案是 CSS 网格,并且考虑到您需要支持 i.a。 IE9,你还需要一个script/fallback来完成这个。

由于 Flexbox 也不能很好地做到这一点,动态地(将需要固定高度或脚本),绝对最简单和最容易维护的解决方案是保留包装器,并且只需来回移动元素一个简单的脚本。

结合脚本在body上加了一个class,我们就可以用CSS来控制元素了。

注1;为了使其更具动态性,可以优化脚本并使用例如元素上的 set 属性,它应该放置的位置,虽然我在这里没有走那么远。

注2;该脚本根据方向切换布局,但可以更新为使用宽度。

注3;调整大小事件使用节流器,我最初 found at MDN,以避免昂贵的操作,例如 DOM 修改。

堆栈片段

(function(d, timeout) {

  function resizing() {
    if (window.innerHeight < window.innerWidth) {
      if (!(d.body.classList.contains('landscape'))) {
        d.body.classList.add('landscape');
        var target = d.querySelector('.right.col');
        target.appendChild(d.querySelector('.b4'));
        target.appendChild(d.querySelector('.b5'));
        target.appendChild(d.querySelector('.b6'));
        target.appendChild(d.querySelector('.b7'));
      }
    } else {
      if (d.body.classList.contains('landscape')) {
        d.body.classList.remove('landscape')
        var target = d.querySelector('.left.col');
        target.insertBefore(d.querySelector('.b4'), d.querySelector('.b2'))
        target.insertBefore(d.querySelector('.b5'), d.querySelector('.b3'))
        target.appendChild(d.querySelector('.b6'));
        target.appendChild(d.querySelector('.b7'));
      }
    }
  }

  /* event handlers */
  window.addEventListener("load", function() {
    resizing();
  }, false);

  window.addEventListener("resize", function() {
    if (!timeout) {
      timeout = setTimeout(function() {
        timeout = null;
        resizing();
      }, 66);
    }
  }, false);

}(document));
.box {
  padding: 1em;
  border: 1px solid black;
  margin: 3px;
}

.left.col {
  float: left;
  width: 100%;
}
.landscape .left.col {          /*  added rule  */
  width: 70%;
}

.right.col {
  float: right;
  width: 30%;
}

.b1 {
  background-color: red;
  height: 150px;
}

.b2 {
  background-color: green;
  height: 80px;
}

.b3 {
  background-color: blue;
  height: 80px;
}

.b4 {
  background-color: yellow;
  height: 40px;
}

.b5 {
  background-color: purple;
  height: 30px;
}

.b6 {
  background-color: cyan;
  height: 40px;
}

.b7 {
  background-color: orange;
  height: 40px;
}
<div class="boxes">
  <div class="left col">
    <div class="box b1">1</div>
    <div class="box b2">2</div>
    <div class="box b3">3</div>
  </div>
  <div class="right col">
    <div class="box b4">4</div>
    <div class="box b5">5</div>
    <div class="box b6">6</div>
    <div class="box b7">7</div>
  </div>
</div>