Keep the middle item centered when side items have different widths

后端 未结 7 1110
长情又很酷
长情又很酷 2020-11-22 17:09

Imagine the following layout, where the dots represent the space between the boxes:

[Left box]......[Center box]......[Right box]

相关标签:
7条回答
  • 2020-11-22 17:39
    1. Use three flex items in the container
    2. Set flex: 1 to the first and last ones. This makes them grow equally to fill the available space left by the middle one.
    3. Thus, the middle one will tend to be centered.
    4. However, if the first or last item has a wide content, that flex item will also grow due to the new min-width: auto initial value.

      Note Chrome doesn't seem to implement this properly. However, you can set min-width to -webkit-max-content or -webkit-min-content and it will work too.

    5. Only in that case the middle element will be pushed out of the center.

    .outer-wrapper {
      display: flex;
    }
    .item {
      background: lime;
      margin: 5px;
    }
    .left.inner-wrapper, .right.inner-wrapper {
      flex: 1;
      display: flex;
      min-width: -webkit-min-content; /* Workaround to Chrome bug */
    }
    .right.inner-wrapper {
      justify-content: flex-end;
    }
    .animate {
      animation: anim 5s infinite alternate;
    }
    @keyframes anim {
      from { min-width: 0 }
      to { min-width: 100vw; }
    }
    <div class="outer-wrapper">
      <div class="left inner-wrapper">
        <div class="item animate">Left</div>
      </div>
      <div class="center inner-wrapper">
        <div class="item">Center</div>
      </div>
      <div class="right inner-wrapper">
        <div class="item">Right</div>
      </div>
    </div>
    <!-- Analogous to above --> <div class="outer-wrapper"><div class="left inner-wrapper"><div class="item">Left</div></div><div class="center inner-wrapper"><div class="item animate">Center</div></div><div class="right inner-wrapper"><div class="item">Right</div></div></div><div class="outer-wrapper"><div class="left inner-wrapper"><div class="item">Left</div></div><div class="center inner-wrapper"><div class="item">Center</div></div><div class="right inner-wrapper"><div class="item animate">Right</div></div></div>

    0 讨论(0)
  • 2020-11-22 17:39

    Here's an answer that uses grid instead of flexbox. This solution doesn't require extra grandchild elements in the HTML like the accepted answer does. And it works correctly even when the content on one side gets long enough to overflow into the center, unlike the grid answer from 2019.

    The one thing this solution doesn't do is show an ellipsis or hide the extra content in the center box, as described in the question.

    section {
      display: grid;
      grid-template-columns: 1fr auto 1fr;
    }
    
    section > *:last-child {
      white-space: nowrap;
      text-align: right;
    }
    
    /* not essential; just for demo purposes */
    section {
      background-color: #eee;
      font-family: helvetica, arial;
      font-size: 10pt;
      padding: 4px;
    }
    
    section > * {
      border: 1px solid #bbb;
      padding: 2px;
    }
    <section>
      <div>left</div>
      <div>center</div>
      <div>right side is longer</div>
    </section>
    
    <section>
      <div>left</div>
      <div>center</div>
      <div>right side is much, much longer</div>
    </section>
    
    <section>
      <div>left</div>
      <div>center</div>
      <div>right side is much, much longer, super long in fact</div>
    </section>

    0 讨论(0)
  • 2020-11-22 17:43

    Instead of defaulting to using flexbox, using grid solves it in 2 lines of CSS without additional markup inside the top level children.

    HTML:

    <header class="header">
      <div class="left">variable content</div>
      <div class="middle">variable content</div>
      <div class="right">variable content which happens to be very long</div>
    </header>
    

    CSS:

    .header {
      display: grid;
      grid-template-columns: [first] 20% auto [last] 20%;
    }
    .middle {
      /* use either */
      margin: 0 auto;
      /* or */
      text-align: center;
    }
    

    Flexbox rocks but shouldn't be the answer for everything. In this case grid is clearly the cleanest option.

    Even made a codepen for your testing pleasure: https://codepen.io/anon/pen/mooQOV

    0 讨论(0)
  • 2020-11-22 17:47

    You can do this like so:

    .bar {
        display: flex;    
        background: #B0BEC5;
    }
    .l {
        width: 50%;
        flex-shrink: 1;
        display: flex;
    }
    .l-content {
        background: #9C27B0;
    }
    .m { 
        flex-shrink: 0;
    }
    .m-content {    
        text-align: center;
        background: #2196F3;
    }
    .r {
        width: 50%;
        flex-shrink: 1;
        display: flex;
        flex-direction: row-reverse;
    }
    .r-content { 
        background: #E91E63;
    }
    <div class="bar">
        <div class="l">
            <div class="l-content">This is really long content.  More content.  So much content.</div>
        </div>
        <div class="m">
            <div class="m-content">This will always be in the center.</div>
        </div>
        <div class="r">
            <div class="r-content">This is short.</div>
        </div>
    </div>

    0 讨论(0)
  • 2020-11-22 17:52

    If the left and right boxes would be exactly the same size, I get the desired effect. However when one of the two is a different size the centered box is not truly centered anymore. Is there anyone that can help me?

    Here's a method using flexbox to center the middle item, regardless of the width of siblings.

    Key features:

    • pure CSS
    • no absolute positioning
    • no JS/jQuery

    Use nested flex containers and auto margins:

    .container {
      display: flex;
    }
    .box {
      flex: 1;
      display: flex;
      justify-content: center;
    }
    
    .box:first-child > span { margin-right: auto; }
    
    .box:last-child  > span { margin-left: auto;  }
    
    /* non-essential */
    .box {
      align-items: center;
      border: 1px solid #ccc;
      background-color: lightgreen;
      height: 40px;
    }
    p {
      text-align: center;
      margin: 5px 0 0 0;
    }
    <div class="container">
      <div class="box"><span>short text</span></div>
      <div class="box"><span>centered text</span></div>
      <div class="box"><span>loooooooooooooooong text</span></div>
    </div>
    <p>&#8593;<br>true center</p>

    Here's how it works:

    • The top-level div (.container) is a flex container.
    • Each child div (.box) is now a flex item.
    • Each .box item is given flex: 1 in order to distribute container space equally (more details).
    • Now the items are consuming all space in the row and are equal width.
    • Make each item a (nested) flex container and add justify-content: center.
    • Now each span element is a centered flex item.
    • Use flex auto margins to shift the outer spans left and right.

    You could also forgo justify-content and use auto margins exclusively.

    But justify-content can work here because auto margins always have priority.

    8.1. Aligning with auto margins

    Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.

    0 讨论(0)
  • 2020-11-22 17:57

    you can also use this simple way to reach exact center alignment for middle element :

    .container {
        display: flex;
        justify-content: space-between;
    }
    
    .container .sibling {
        display: flex;
        align-items: center;
        height: 50px;
        background-color: gray;
    }
    
    .container .sibling:first-child {
        width: 50%;
        display: flex;
        justify-content: space-between;
    }
    
    .container .sibling:last-child {
        justify-content: flex-end;
        width: 50%;
        box-sizing: border-box;
        padding-left: 100px; /* .center's width divided by 2 */
    }
    
    .container .sibling:last-child .content {
        text-align: right;
    }
    
    .container .sibling .center {
        height: 100%;
        width: 200px;
        background-color: lightgreen;
        transform: translateX(50%);
    }
    
    

    codepen: https://codepen.io/ErAz7/pen/mdeBKLG

    0 讨论(0)
提交回复
热议问题