18

Problem Summary

My desired layout is to be able to center (both vertically and horizontally) an image of unknown size on the page. If the image is too big to fit in either direction, I want to show scroll bars so the user can scroll to see the full image. The issue I've run in to is that when the image is too big to fit, the top and left of the image (depending on what portion is cut off) will never be able to be scrolled to.

Attempted Solution

I am attempting to use flexbox to achieve the desired layout, but flexbox is not a requirement. Here is a small example that reproduces the problem (note that I haven't put any browser prefixes in the CSS, so you will want to view this in Chrome [or maybe Firefox, too?]):

.body {
  height: 600px;
}

.container {
  margin: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid red;
  height: 100%;
  overflow: auto;
}

img {
  border: 5px solid black;
}
<div class="body">
  <div class="container">
    <img src="http://placehold.it/700x700" />
  </div>
</div>

You should be able to see the border completely around the image, but the left and/or top part of the image will get cut off as the window shrinks. More and more of the image will become un-viewable as the window continues to shrink.

Note that it doesn't seem to matter if there is an image in there. Here's a snippet just using plain-old divs to show the problem:

.body {
  height: 600px;
  margin-top: 80px;
}

.container {
  margin: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid red;
  height: 100%;
  overflow: auto;
}

.content {
  border: 5px solid black;
  width: 600px;
  height: 600px;
  background-color: gray;
}
<div class="body">
  <div class="container">
    <div class="content"></div>
  </div>
</div>

Again the entire div should always be reachable by scrolling, but it is not.

Question

How can I achieve the desired layout described above using only HTML and CSS (JS answers not accepted)? Flexbox is not required to solve the problem, but it would be a nice-to-have.

Thanks!

Chandler Kent
  • 363
  • 2
  • 8
  • If you're centering an image that is bigger than the viewport, the top and left will always be cutoff. You cannot scroll past the top or left side of the viewport. You need to alter the way the image is being handled at those sizes in order to prevent this from happening, likely through media queries that prevent the image from being centered or plain old shrinking the image to fit the viewport. If it has to be centered at all times, you'll have to not center the image and use js to scroll to the center as the viewport shrinks. – brouxhaha Sep 11 '15 at 16:56
  • Can you clarify what you mean @brouxhaha? If the image is too big for the viewport and the container element has scroll set, I would expect to be able to scroll to all the content in the container. – Chandler Kent Sep 14 '15 at 13:37
  • As you can with intrinsic measurements like `min-content`, `max-content`, and `fit-content`. But they're not well known or used very often ... I have no idea why. I'm sure there's other methods but they elude me. – zer00ne Sep 14 '15 at 18:52
  • Possible duplicate of [How to vertically center a div for all browsers?](http://stackoverflow.com/questions/396145/how-to-vertically-center-a-div-for-all-browsers) – jaunt Feb 07 '16 at 08:50

4 Answers4

17

Just remove the justify-content from your .container and add a margin: auto to your image.

.body {
  height: 600px;
}

.container {
  margin: auto;
  display: flex;
  /* align-items: center; // no need for this anymore */
  /* justify-content: center; // remove this */
  border: 1px solid red;
  height: 100%;
  overflow: auto;
}

img {
  border: 5px solid black;
  margin: auto;  /* add this */
}
<div class="body">
  <div class="container">
    <img src="http://placehold.it/700x700" />
  </div>
</div>
Dennis
  • 206
  • 2
  • 7
3

You can achieve this by wrapping your image in a container within a flex container, as follows:

  • Flex container
    • display: flex
    • height: 100vh
  • Inner container
    • margin: auto

This will allow you to get an image of unknown size centered in the viewport both vertically and horizontally, without losing the ability to scroll to any part of the image in case it doesn't fit inside the viewport in either direction.

Example 1: (Image centered in viewport)

body {
  margin: 0;  /* Override user agent styles */
}

.flex {
  display: flex;
  height: 100vh;
}
.container {
  margin: auto;
}
<body class="flex">
  <main class="container">
    <img src="http://placehold.it/100x100">
  </main>
</body>

Example 2: (Image doesn't fit in viewport, yet can still be scrolled)

body {
  margin: 0;  /* Override user agent styles */
}

.flex {
  display: flex;
  height: 100vh;
}
.container {
  margin: auto;
}
img {
  border: 1px solid red;
}
<body class="flex">
  <main class="container">
    <img src="http://placehold.it/1000x1000">
  </main>
</body>

It works in the latest version of Chrome, Firefox, Opera, and Edge.

Chava Geldzahler
  • 3,605
  • 1
  • 18
  • 32
0

So the criteria is that you always want a centered image vertically and horizontally, and have it scrollable no matter how small or large the viewport is? Take a look at this snippet and let me know if I'm close. If I'm totally wrong then I have plan B. If I have succeeded or almost there, I'll explain what I did.

SUMMARY

  1. Define <body> because if you don't then you can't dictate how far your scrollbars can go.

    position: relative, top, bottom, right, and left at 0 stretches the <body> to cover the viewport.

    width and height of body are 100vw and 100vhenforcing a limit on <body>

    ♦ Since <body> is a container all flexbox properties will influence .x for centering horizontally.

    Since you emphasized the use of scrollbars, overflow: scroll scroll bars will persist even if not needed. Which is good in circumstances involving dynamic content because there wouldn't be any jumping.

  2. The outer container .x has flexbox properties to center the inner container .y. In addition you'll notice a very little known property.

  3. .y has flex-flow: column nowrap to center the image.

  4. body has min-content applied to both height and width which will force it's content's borders into a tight package. This is how you're able to see the image's borders without the usual cutoff.

UPDATE

This demo has some simple JS to demonstrate that images of any size are:

  1. Centered horizontally
  2. Centered vertically
  3. All sides can be viewed at any size.

Note: Anything under DEMO is entirely safe to delete or comment out.

// DEMO 
var s = document.getElementById('s');
s.addEventListener('click', function(event) {
  event.preventDefault();
  var w = document.getElementById('w'),
    h = document.getElementById('h'),
    pic = document.getElementById('pic'),
    wv = w.value,
    hv = h.value;
  pic.src = "http://placehold.it/" + wv + "x" + hv + "/";
}, false);
/* RESET */

html {
  box-sizing: border-box;
  font: small-caps 400 16px/1.45'Source Code Pro';
}
*,
*:before,
*:after {
  box-sizing: inherit;
  margin: 0;
  padding: 0;
  border: 0 solid transparent;
}
/* REQUIRED */

body {
  position: relative;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  width: 100vw;
  height: 100vh;
  overflow: auto;
  margin: auto;
  width: -moz-min-content;
  width: -webkit-min-content;
  width: min-content;
  height: -moz-min-content;
  height: -webkit-min-content;
  height: min-content;
}
.x {
  height: 600px;
  flex-flow: row nowrap;
  display: flex;
  align-items: center;
  justify-content: center;
  align-content: center;
}
.y {
  margin: auto;
  display: flex;
  flex-flow: column nowrap;
  align-items: center;
  justify-content: center;
  align-content: center;
  border: 2px dashed red;
}
/* DEMO */

.ui {
  position: fixed;
  top: 1em;
  left: 1em;
  width: 500px;
  line-height: 1.3;
  z-index: 1;
}
fieldset {
  border: 1px solid lightgrey;
  border-radius: 6px;
}
input {
  width: 48px;
  line-height: 1.2;
  border: 1px solid grey;
  border-radius: 6px;
  padding: 2px;
  margin: 2px 1px;
}
label {
  margin: 2px 0 2px 2px;
}
<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <title>C.R.A.P.D. -=- Changing Rectangle Alignment, Position, and Dimensions</title>
</head>

<body>
  <!-- DEMO -->
  <header class="ui">
    <fieldset>
      <legend>Changing Rectangle Alignment Position and Dimensions</legend>
      <label for="w">Width
        <input id="w" type="text" value="300" />
      </label>
      <label for="h">Height
        <input id="h" type="text" value="300" />
      </label>
      <input id="s" type="submit" value="Submit" />
    </fieldset>
  </header>
  <!-- REQUIRED -->
  <main class="x">
    <section class="y">
      <img id="pic" src="http://placehold.it/300x300/" />
    </section>
  </main>
</body>

</html>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Thanks @zer00ne. This solution doesn't appear to be vertically centered. – Chandler Kent Sep 11 '15 at 19:15
  • View it full page. The top and bottom are at full viewport, that is in essence centered. – zer00ne Sep 11 '15 at 19:16
  • My bad. You are correct , sir! I just looked at the snippet on my phone portrait oriented and indeed it wasn't vertically centered. I have rectified this, please see updated snippet. – zer00ne Sep 11 '15 at 19:32
  • I still don't see that this gets vertically centered. Try changing the image height to 300px for example. – Chandler Kent Sep 11 '15 at 21:32
  • @ChandlerKent I got a demo at 2400x2400 as well – zer00ne Sep 11 '15 at 22:40
  • thanks again. Your latest version is now not centered horizontally. Also, the scroll bars appear even before necessary (meaning, if the image is 240 x 240 the scroll bars appear even when the width and height of the browser window are larger than 240 pixels). – Chandler Kent Sep 14 '15 at 12:47
-1

Found a solution with double flex container:

.body {
  height: 600px;
  overflow: auto;
  display: flex;
  flex-direction: column;
}
.container {
  display: flex;
  align-items: center;
  flex-grow: 1;
}
.content {
  flex-grow: 1;
  text-align: center;
}
img {
  border: 1px solid red;
}
<div class="body">
 <div class="container">
    <div class="content">
      <img src="http://placehold.it/700x700" />
    </div>
  </div>
</div>
maxime schoeni
  • 2,666
  • 2
  • 18
  • 19