Search code examples
csslayoutflexboxcss-floatcentering

Vertical centering of float elements (navigation controls) within a container


I've read How can I center two floated elements within a container? and friends; the former refers to horizontal centering, and the friends to non-floated children AFAICT. Why am I using floats? Well, read on...

Here's my desired layout:

+-------+-------------------+-------+
|       | ##### ##### ##### |       |
| [ < ] | ##### ##### ##### | [ > ] |
|       | ##### ##### ##### |       |
+-------+-------------------+-------+

The < and > are navigation arrows; # are dynamically loaded image thumbnails within a slick carousel. The images are all the same width & height, but the carousel may be configured to display more than three such images. Also, the whole thing of course needs to be responsive :)

My issue is that I am unable to vertically center the elements containing the navigation elements within the outer container. Various combinations of float: left/none, display: flex-box, overflow: auto/ hidden, margin: auto, etc etc have been tried and I am at my wit's end.

With no float, the navigation elements and carousel stack up (as expected):

+-------+-------------------+-------+
| [ < ]                             | 
| ##### ##### #####                 |
| ##### ##### #####                 |
| ##### ##### #####                 |
| [ > ]                             |
+-------+-------------------+-------+

With float: left, the nav elements refuse to center vertically - it looks like those elements don't receive the full height of the container (nav elements are divs containing a span):

+-------+-------------------+-------+
| [ < ] | ##### ##### ##### | [ > ] |
|       | ##### ##### ##### |       |
|       | ##### ##### ##### |       |
+-------+-------------------+-------+

Here's the basic (non-working) code. I've stubbed out the carousel for ease of testing. In the below code I'm using javascript just to simulate loading the (remote) images.

$('document').ready(function() {
  setTimeout(function() {
    $('img').css({
      width: '150px',
      height: '110px'
    });
  }, 1500);
});
.outer {
  position: relative;
  border: 1px solid black;
}

.nav {
  position: relative;
  border: 1px solid green;
  width: 4%;
  height: 100%;
  float: left;
  display: flex-box;
  align-content: center;
}

.carousel {
  position: relative;
  border: 1px solid red;
  width: 90%;
}

img {
  background: lightblue;
  display: block;
}

.image {
  position: relative;
  float: left;
  padding-left: 12px;
  padding-right: 12px;
  padding-top: 6px;
  padding-bottom: 6px;
  height: auto;
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer">
  <div class="left nav">
    <span class="left nav-arrow">&lt;</span>
  </div>
  <div class="carousel">
    <div class="image"> <img class="img1"> </div>
    <div class="image"> <img class="img2"> </div>
    <div class="image"> <img class="img3"> </div>
  </div>
  <div class="right nav">
    <span class="right nav-arrow">&gt;</span>
  </div>
</div>

I'm not against a JS solution though would prefer a pure CSS one if possible. Also I'm not a CSS developer by day so there may be some ahem cargo-cult CSS rules here.


Solution

  • You have to set display: flex to your outer element as well. I updated your fiddle and simplified the css.

    $('document').ready(function() {
      setTimeout(function() {
        $('img').css({
          width: '150px',
          height: '110px'
        });
      }, 1500);
    });
    .outer {
      border: 1px solid black;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    .nav {
      border: 1px solid green;
      margin: 0 5px;
    }
    
    .carousel {
      border: 1px solid red;
      flex-grow: 1;
      display: flex;
      justify-content: center;
    }
    
    img {
      background: lightblue;
    }
    
    .image {
      padding: 6px 12px;
      border: 1px solid black;
    }
    
    .nav-arrow {
      padding: 5px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="outer">
    
      <div class="left nav">
        <span class="left nav-arrow">&lt;</span>
      </div>
    
      <div class="carousel">
    
        <div class="image"> <img class="img1"> </div>
        <div class="image"> <img class="img2"> </div>
        <div class="image"> <img class="img3"> </div>
    
      </div>
    
      <div class="right nav">
        <span class="right nav-arrow">&gt;</span>
      </div>
    
    </div>

    I added the snippet to my answer.