如何使用flexbox布局多列?

How to use flexbox to layout multiple columns?

我正在尝试按以下方式布置多列。我正在使用 Wordpress,但编辑 PHP、HTML、CSS 等。

这里是HTML。将 A B C 想象成没有固定高度和百分比宽度的代码块,如图所示。

<div class="flex wrap">
     <div class="main">A</div>
     <div class="optional">B</div>
     <div class="main2">C</div>
</div>

只要我在使用 flexbox,我就可以使用视口来改变 flex 的顺序。但是现在我无法获得所需的桌面布局。

我可以使用带有嵌套 div 的列 (bootstrap) 作为第二列来创建桌面布局,但移动布局将是 BAC 或 ACB 而不是 ABC。

这是我的 CSS:

.flex.wrap {
    flex-wrap: wrap;
}

.flex {
    display: flex!important; /* to override display:block */
}

.main {
    order: 2;
    flex: 0 75%;
}

.optional {
    order: 1;
    flex: 0 25%;
}
.main2 {
    order: 3;
    flex: 0 75%
}

我做了研究,看到很多这样的问题被问到,但没有满足我需要的满意答案。

我不必使用 flex box,也不需要纯粹的 css 解决方案 - 只要它可以在大多数现代浏览器和移动设备上运行即可。我只需要有用的东西。所以我愿意接受任何建议。

我也可以在两个代码块上使用 display: none,但这会使代码增加 67 行,我认为这是不必要的。

即使有一个 php 解决方案可能像下面那样工作,它也可能会工作。

If (code that determines mobile viewport) echo html
else
echo nothing

还有另一个相同的代码,用于我希望桌面版本所在的位置。我知道这并不优雅,但最后我关心的是结果,包括性能而不是其他任何东西。

任何帮助将不胜感激。

因为你不能设置固定的高度,Flexbox 不能单独做到这一点,所以你要么将它与定位或脚本结合起来,要么使用 CSS Grid(虽然浏览器支持比 Flexbox 少) .

这是一个使用定位的示例,并更新了原始代码示例中的 classes 以使其更易于理解。

html, body {
  margin: 0;
}
.wrapper {
  position: relative;
}
.flex {
  display: flex;
  flex-direction: column;
}
.flex > div {
  border: 1px solid;
  box-sizing: border-box;
}

/*  for desktop  */
@media screen and (min-width: 600px) {
  .flex > .optional {
    position: absolute;
    left: 0;
    top: 0;
    width: 25%;
    height: 100%;
  }
  .flex .main,
  .flex .main2 {
    margin-left: 25%;    /*  match the width of the "optinal"  */
  }
}
<div class="wrapper">
  <div class="flex">
    <div class="main">A</div>
    <div class="optional">B</div>
    <div class="main2">C</div>
  </div>
</div>


已更新

在某些情况下,根本无法使用绝对定位,例如,当左右列(在桌面模式下)都可能超出另一列时。

这是一个使用小脚本的版本,它模仿媒体查询并将 optional 元素移入和移出 flex 容器。

它还在 body 上切换 class,在本例中为 mobileview,它在 CSS 中用于切换 wrapper 作为一个弹性容器并在 optional 上设置适当的属性,当它是 wrapper 而不是 flex.

的弹性项目子项时

使用脚本中的 minwidth = 600 变量可以控制布局切换的宽度。

这里有一个 fiddle demo 也可以一起玩

(function(d, w, timeout) {

  /* custom variables */
  var flexcontainer = '.flex',
    flexitem = '.optional',
    minwidth = 600,             /* if null, then when viewport is portrait */
    classname = 'mobileview';
    
  /* here happens the magic */
  function resizeing() {
    if ((minwidth && (minwidth < w.innerWidth)) ||
        (!minwidth && (w.innerHeight < w.innerWidth))) {
      if (!(d.body.classList.contains(classname))) {
        /* move it outside the main flexcontainer */
        d.body.classList.add(classname);
        var fca = qSA(flexcontainer);
        for (var i = 0; i < fca.length; i++) {
          fca[i].parentNode.appendChild(qS(flexitem, fca[i]))
        }
      }
    } else {
      if (d.body.classList.contains(classname)) {      
        /* move it back inside the main flexcontainer */
        d.body.classList.remove(classname)
        var fca = qSA(flexcontainer);
        for (var i = 0; i < fca.length; i++) {
          fca[i].appendChild(qS(flexitem, fca[i].parentNode))
        }
      }      
    }
  }
  
  /* run at page load init resize */
  w.addEventListener("load", function() {
    resizeing();
  }, false);
  
  /* grab when viewport resize */
  w.addEventListener("resize", function() {
    if (!timeout) {
      timeout = setTimeout(function() {
        timeout = null;
        resizeing();
      }, 66);
    }
  }, false);
  
  /* helper variables */
  var qSA = function(s, el) {
      return (el) ? el.querySelectorAll(s) :
        d.querySelectorAll(s)
    },
    qS = function(s, el) {
      return (el) ? el.querySelector(s) :
        d.querySelector(s)
    };
}(document, window));
html, body {
  margin: 0;
}
.wrapper .flex {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}
.wrapper .flex > div {
  flex-grow: 1;
  border: 1px solid;
  box-sizing: border-box;
}
.wrapper .flex .optional {
  order: 1;
}
.wrapper .flex .main2 {
  order: 2;
}

/*  for desktop  */
.mobileview .wrapper {
  display: flex;
}
.mobileview .wrapper .optional {
  flex-basis: 25%;
  order: -1;
  border: 1px solid;
  box-sizing: border-box;
}
<div class="wrapper">
  <div class="flex">
    <div class="main">A</div>
    <div class="optional">
      B as a lot more content<br>
      B as a lot more content<br>
      B as a lot more content<br>
      B as a lot more content<br>
      B as a lot more content<br>
    </div>
    <div class="main2">C</div>
  </div>
</div>

<h5>More than one container and other text etc.</h5>

<div class="wrapper">
  <div class="flex">
    <div class="main">
      A as a lot more content<br>
      A as a lot more content<br>
      A as a lot more content<br>
      A as a lot more content<br>
      A as a lot more content<br>
    </div>
    <div class="optional">B</div>
    <div class="main2">C</div>
  </div>
</div>

实现此布局的最有效方法是使用 CSS 网格布局:

.container {
  display: grid;
  grid-template-columns: 25% 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  grid-gap: 5px;
  grid-template-areas: " B A " 
                       " B C ";
}

.A { grid-area: A; }
.B { grid-area: B; }
.C { grid-area: C; }

@media ( max-width: 800px ) {
  .container {
    grid-template-columns: 1fr;
    grid-template-areas: "A" "B" "C";
  }
}

/* non-essential styles; for demo only */
.container { height: 200px; }
.A { background-color: aqua; }
.B { background-color: gold; }
.C { background-color: lightgreen; }
.container > div { display: flex; align-items: center; justify-content: center; }
<div class="container">
  <div class="A">A</div>
  <div class="B">B</div>
  <div class="C">C</div>
</div>

jsFiddle demo


工作原理如下:

  • grid-template-columns establishes the number of columns. Same concept for grid-template-rows中值的数量。
  • grid-template-areas 属性 允许您使用 ASCII 视觉艺术来安排布局。将网格区域名称(为每个元素定义)放在您希望它们出现的位置。
  • fr单元告诉列/行消耗可用的space。它类似于 flex-grow

此外,原始 HTML 结构没有变化。它保持尽可能简单。而且不需要使用 order 属性.


浏览器支持 CSS 网格

  • Chrome - 截至 2017 年 3 月 8 日的全面支持(版本 57)
  • Firefox - 截至 2017 年 3 月 6 日的全面支持(版本 52)
  • Safari - 截至 2017 年 3 月 26 日的全面支持(版本 10.1)
  • Edge - 截至 2017 年 10 月 16 日的全面支持(版本 16)
  • IE11 - 不支持当前规范;支持过时版本

完整图片如下:http://caniuse.com/#search=grid


Flexbox 的问题

你想要的桌面布局可以用flexbox来实现如果你能在容器上设置一个固定的高度

使用flex-flow: column wrap,这将允许一个项目消耗第一列中的所有space。然后第二项和第三项可以换行并共享第二列中的 space。您可以使用 order 属性 重新排列每个项目的位置。

但是,如前所述,容器上的固定高度是必要的,否则项目将没有断点并且不会换行。

在行方向上,桌面布局无法使用 flexbox,因为您要求一个项目换行在同一行中的另一个项目之下。换句话说,"B" 确定了行的高度,然后您希望 "C" 换行到该行的 "A" 下。这不是 flexbox 的工作方式。

这里有更完整的解释:

  • Is it possible for flex items to align tightly to the items above them?

更多的是信息而不是有效的答案此时

您可以从 flex 切换到 grid,其中

  • grid 允许设置行和列,排序也可以跨越的元素,
  • flex 允许重新排序:

例子

body {
  display: grid;
  grid-template-columns: 25% 1fr;
  grid-gap: 10px
}

.opt {
  grid-column: 1;
  grid-row: 1 / span 2;
  background: gold
}

div {
  background: turquoise
}

@media all and (max-width: 360px) {/* set here width where you need to switch layout */
  body {
    display: flex;
    flex-flow: column
  }
  .a {
    order: -2
  }
  .opt {
    order: -1
  }
  div {
    margin: 5px 10px
  }
}
<div class="a">A of any height</div>
<div class="c">C of any height</div>
<div class="opt">B of any height</div>

如果您觉得网格很有趣:https://css-tricks.com/snippets/css/complete-guide-grid/