【问题标题】:Complex CSS layout – flexbox or grid?复杂的 CSS 布局——flexbox 还是网格?
【发布时间】:2020-09-28 08:03:39
【问题描述】:

我希望实现一个复杂的布局(参见下面的图 1-4),其中包含三个主要元素:标题、侧边元素和标签。标题应该跨越整个宽度,侧元素应该浮动到右侧并跨越容器的整个高度,然后标签(可能有很多)应该包裹在标题下。

 ___________________________   ____
|                           | |    |
|   lorem ipsum dolor ...   | |    |
|___________________________| |    |
 ______   _______   ______    | XX |
|      | |       | |      |   |    |
|  ab  | |  cde  | |  fg  |   |    |
|______| |_______| |______|   |____|

先决条件:

  1. .title 应尽可能
  2. .side.tag 的宽度应与其内容一样宽,尽可能
  3. 任何元素的大小都是未知的,因此我不能使用固定的宽度或百分比(显然 100% 除外)。
  4. 该解决方案应适用于任何容器宽度。
  5. .side.tag可选的,因此如果缺少它们,解决方案应该可以工作。
  6. 所有元素都是兄弟元素,这意味着 例如,我不能将所有 .tag 包装在 div 中。
  7. .side 元素在源代码中始终位于最后,因此下面的 order 属性。

请查看我要介绍的用例:

.container {
  display: flex;
  flex-flow: row wrap;
  width: 100%;
}
.title {
  order: 1;
  flex-grow: 1;
  flex-basis: 100%;
}
.side, .tag {
  flex-grow: 0;
  flex-basis: auto;
}
.side {
  order: 2;
}
.tag {
  order: 3;
}

/* sample styling - ignore */
.container { width: 100%; }
.title, .tag, .side { padding: 30px; margin: 5px; color: white; font-family: Arial; font-size: 24px; }
.title { background: coral; }
.tag { background: cornflowerblue; }
.side { background: mediumseagreen; }
<h2>Case 1</h2>
<div class='container'>
  <div class='title'>
    Lorem ipsum dolor sit amet...
  </div>
  <div class='tag'>abc</div>
  <div class='tag'>def</div>
  <div class='tag'>ghi</div>
  <div class='side'>XX</div>
</div>

<h2>Case 2</h2>
<div class='container'>
  <div class='title'>
    Lorem ipsum dolor sit amet...
  </div>
  <div class='tag'>abc</div>
  <div class='tag'>def</div>
  <div class='tag'>ghi</div>
  <div class='tag'>jkl</div>
  <div class='tag'>mno</div>
  <div class='tag'>pqr</div>
  <div class='side'>XX</div>
</div>

<h2>Case 3</h2>
<div class='container'>
  <div class='title'>
    Lorem ipsum dolor sit amet...
  </div>
  <div class='tag'>abc</div>
  <div class='tag'>def</div>
</div>

<h2>Case 4</h2>
<div class='container'>
  <div class='title'>
    Lorem ipsum dolor sit amet...
  </div>
  <div class='side'>XX</div>
</div>

我的问题

实现这种复杂的灵活布局的最佳方式是什么?我可以使用 flexbox 来使上述所有用例都能正常工作,还是应该更多地研究网格?

谢谢!

【问题讨论】:

    标签: html css layout flexbox css-grid


    【解决方案1】:

    我怀疑 flexbox 能否满足所有要求。

    这是一个几乎没有缺点的 CSS 网格近似:

    • 所有标签都将具有相同的宽度,并且宽度由容器而不是其内容控制
    • 如果缺少侧面元素,我们将有一个空单元格(正在努力寻找解决方案)

    .container {
      display: grid;
      border:1px solid;
      grid-template-columns:repeat(auto-fit,minmax(100px,1fr));
    }
    
    .title {
      grid-column:1 / -2;
    }
    
    .title + .tag {
      grid-column:1;
    }
    
    .side {
      grid-column:-2;
      grid-row: 1/span 1000;
    }
    
    
    /* sample styling - ignore */
    
    .title,
    .tag,
    .side {
      padding: 30px;
      margin: 5px;
      color: white;
      font-family: Arial;
      font-size: 24px;
    }
    
    .title {
      background: coral;
    }
    
    .tag {
      background: cornflowerblue;
    }
    
    .side {
      background: mediumseagreen;
    }
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='side'>XX</div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='tag'>abc</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>gh</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='side'>XX</div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='tag'>abc</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
    </div>

    根据评论,这里是一个侧元素只占一行的解决方案:

    .container {
      display: flex;
      flex-wrap:wrap;
      border:1px solid;
    }
    
    .title {
      order:-2;
      flex-grow:1;
      flex-basis:0;
      min-width:0;
    }
    
    
    .side {
      order:-1;
    }
    .container:before {
      content:"";
      flex-basis:100%;
    }
    
    /* sample styling - ignore */
    
    .title,
    .tag,
    .side {
      padding: 30px;
      margin: 5px;
      color: white;
      font-family: Arial;
      font-size: 24px;
    }
    
    .title {
      background: coral;
    }
    
    .tag {
      background: cornflowerblue;
    }
    
    .side {
      background: mediumseagreen;
    }
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='side'>XX</div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='tag'>abc</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>gh</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='side'>XX XX</div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='tag'>abc</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
    </div>

    由于我觉得单靠 CSS 无法做到这一点,这里有一个 JS 解决方案:

    var cs = document.querySelectorAll('.container');
    
    cs.forEach((a) => {
      var side = a.querySelector('.side');
      if(side)
        a.style.paddingRight = (side.offsetWidth + 10) + "px";
    });
    .container {
      display: flex;
      flex-wrap:wrap;
      border:1px solid;
      padding-right: 0px;
      position:relative;
    }
    
    .title {
      flex-basis:100%;
    }
    
    .side {
      position:absolute;
      top:0;
      bottom:0;
      right:0;
    }
    
    /* sample styling - ignore */
    
    .title,
    .tag,
    .side {
      padding: 30px;
      margin: 5px;
      color: white;
      font-family: Arial;
      font-size: 24px;
    }
    
    .title {
      background: coral;
    }
    
    .tag {
      background: cornflowerblue;
    }
    
    .side {
      background: mediumseagreen;
    }
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='side'>XX</div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='tag'>abc</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>gh</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='side'>XX XX</div>
    </div>
    
    <div class='container'>
      <div class='title'>
        Lorem ipsum dolor sit amet...
      </div>
      <div class='tag'>abc</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
      <div class='tag'>def</div>
      <div class='tag'>ghi</div>
    </div>

    【讨论】:

    • 有没有办法让所有列缩小以适应其内容标题?不幸的是,标签可以有各种长度的内容(例如 4 对 16 个字符长)所以我不确定我是否可以让它们都具有相同的宽度......
    • @philfriday 无法使用 CSS 网格。做你想做的事的唯一方法需要一些 JS。如果你同意的话,一个小脚本可以帮助你满足所有要求吗?
    • 对不起,试图避免使用 JS 来做这个......听听这个:如果侧元素只垂直跨越一行,我可以使用 flexbox 吗,考虑到所有其他变量都是相同的?也就是说,第一行是标题和边元素(XX),第二行是所有标签。
    • @philfriday 是的,这很容易:jsfiddle.net/brjpz4L3
    • 感谢@Temani Afif,看起来很棒!缩小视口时,有没有办法防止 XX 元素换行?我想让它始终保持在第一行。
    【解决方案2】:

    我更改了 HTML,这是我的解决方案,请检查。 希望对您有所帮助:)

    .container {
                display: flex;
                width: 100%;
            }
            .title {
                order: 1;
                flex-grow: 1;
                flex-basis: 100%;
            }
            .side, .tag {
                flex-grow: 0;
                flex-basis: auto;
            }
            .side {
                order: 2;
            }
            .tag {
                order: 3;
            }
            .content-page {
                width: 100%;
            }
    
            .content-tags {
                display: flex;
                flex-wrap: wrap;
            }
    
            /* sample styling - ignore */
            .container { width: 100%; }
            .title, .tag, .side { padding: 30px; margin: 5px; color: white; font-family: Arial; font-size: 24px; }
            .title { background: coral; }
            .tag { background: cornflowerblue; }
            .side { background: mediumseagreen; }
    <div class='container'>
        <div class="content-page">
            <div class='title'>
                Lorem ipsum dolor sit amet...
            </div>
            <div class="content-tags">
                <div class='tag'>abc</div>
                <div class='tag'>def</div>
                <div class='tag'>ghi</div>
                <div class='tag'>def</div>
                <div class='tag'>ghi</div>
                <div class='tag'>abc</div>
                <div class='tag'>def</div>
                <div class='tag'>ghi</div>
                <div class='tag'>def</div>
                <div class='tag'>ghi</div>
                <div class='tag'>def</div>
                <div class='tag'>ghi</div>
                <div class='tag'>def</div>
                <div class='tag'>ghi</div>
            </div>
        </div>
    
    
        <div class='side'>XX</div>
    </div>

    【讨论】:

    • 谢谢,但很遗憾我无法更改 HTML 标记并将标签放入自己的 div...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多