如何使用flexbox布局多列?

3
我正在尝试以以下方式布局多列。我正在使用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 的顺序。但是现在我无法获得所需的桌面布局。
我可以使用列(bootstrap)和嵌套 div 来创建第二列的桌面布局,但这样移动端的布局将变成 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

我希望在桌面版本中也能使用相同的代码。我知道这并不优雅,但最终我关心的是结果,包括性能,而不是其他任何东西。

非常感谢您的任何帮助。

3个回答

3

由于Flexbox不能设置固定高度,所以它不能单独完成此任务,因此您可以将其与定位或脚本结合使用,或者使用CSS Grid(但是它的浏览器支持不如Flexbox)。

以下是一个使用定位的示例,并使用您原始代码示例中的类使其更易于理解。

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 上切换一个类,例如 mobileview,CSS 中使用该类来将 wrapper 切换为 flex 容器,并在 optional 成为 wrapper 的 flex 子项时设置适当的属性,而不是作为 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>


[code]html,body { margin: 0; } .wrapper { position: relative; } .flex { display: flex; flex-direction: column; } .flex > div { border: 1px solid; box-sizing: border-box; }@media screen and (min-width: 992px) { .flex > div:nth-child(2) { position: absolute; left: 0; top: 0; width: 30%; } .flex > div:nth-child(1), .flex > div:nth-child(3) { margin-left: 30%; } }[/code] - Saud Kazia
这很棒。您能否编辑问题以显示“如何”工作,以便我可以理解第n个的关系,并在有更多列的情况下进行更改。这样我就不需要顺序了。它在桌面和移动设备上都可以使用。是否有任何副作用? - Saud Kazia
您可以在https://www.bizbuz.in/directory/list/other/d-lights/上看到问题。基本上,我需要“清除”flexbox,以便我可以在下面添加更多内容。 - Saud Kazia
不用考虑大图片。 - Saud Kazia
是的,除了最后一个 div 不应该是 flexbox 的一部分。这可能吗?原因是有多个 PHP 文件(WordPress),我需要确保 flexbox 包含在一个 PHP 文件中,而不会溢出到之前或之后的内容,否则我将无法轻松管理内容。 - Saud Kazia
显示剩余11条评论

3

现在更多的是提供信息而不是高效的答案。

您可以从 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/

谢谢。但我认为之前的答案对我有效,而且可能得到更好的支持。 - Saud Kazia

1
最高效的实现此布局的方法是使用CSS Grid Layout

.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>

{{链接1:jsFiddle演示}}


它是如何工作的:

  • grid-template-columns 中的值的数量确定了列数。grid-template-rows 同理。
  • grid-template-areas 属性允许您使用 ASCII 可视化艺术来排列布局。将网格区域名称(为每个元素定义)放置在您想要它们出现的位置。
  • fr 单位告诉列/行使用可用空间。它类似于 flex-grow

此外,原始 HTML 结构不会更改。它尽可能简单。无需使用 order 属性。


CSS Grid的浏览器支持

  • 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,这将允许一个项目在第一列中占用所有空间。然后,第二个和第三个项目可以换行并共享第二列中的空间。您可以使用order属性重新排列每个项目的位置。

但是,如上所述,容器上必须有固定的高度,否则项目没有断点,将无法换行。

在行方向上,使用flexbox无法实现桌面布局,因为您要求一个项目在同一行中包裹另一个项目下面。换句话说,“B”建立了该行的高度,然后您希望“C”在该行中在“A”下面包装。这不是flexbox的工作方式。

以下是更详细的说明:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接