16

I want separate scroll on each of my columns in my grid layout.

Currently, I am developing a mobile only web application. I want to use a different grid layout for the portrait and landscape orientations.

The portrait orientation is just 1 column and every element is after the other. No problem here.

In the landscape orientation I want to use 2 columns. My whole content is displayed on the left side and my navigation moves to the right side. Now I want both parts to have a separate scroll. Is there a way to implement this? And the scroll should stop if the content of the current column ends.

Code on CodePen: https://codepen.io/SuddenlyRust/pen/rmJOqV

.grid-container {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-gap: 15px 0;
}

header {
  background-color: green;
  grid-column: 1;
  grid-row: 1
}

main {
  background-color: blue;
  grid-column: 1;
  grid-row: 2;
}

nav {
  background-color: pink;
  grid-column: 1;
  grid-row: 3;
}

footer {
  background-color: teal;
  grid-column: 1;
  grid-row: 4;
}

@media only screen and (orientation: landscape) {
  .grid-container {
    grid-template-columns: 5fr 4fr;
  }
  nav {
    grid-column: 2;
    grid-row: 1 / span 3;
  }
  footer {
    grid-row: 3;
  }
}

h1 {
  min-height: 200px;
}
<div class="grid-container">
  <header>
    <h1>Logo</h1>
  </header>
  <main>
    <h1>content</h1>
  </main>
  <nav>
    <h1>Navigation</h1>
  </nav>
  <footer>
    <h1>Footer</h1>
  </footer>
</div>

Thank you very much for your time!

Asons
  • 84,923
  • 12
  • 110
  • 165
ruslansteiger
  • 462
  • 1
  • 8
  • 21

2 Answers2

15

In the landscape orientation I want to use 2 columns. My whole content is displayed on the left side and my navigation moves to the right side. Now I want both parts to have a separate scroll. Is there a way to implement this? And the scroll should stop if the content of the current column ends.

In the left column you have three separate grid items: the header, main and footer elements.

In the right column you have one grid item: the nav element.

Adding a scrollbar – vertical or horizontal – to the left column is not feasible because there are three separate elements. You would need to wrap all elements in a container for a single scrollbar to work.

Adding a scrollbar – vertical or horizontal – to the right column is pretty easy because there is only one element.

Assuming that you're talking about a vertical scrollbar, here's one way to make it work:

body {
  margin: 0;
}

.grid-container {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-gap: 15px 0;
  height: 100vh;
}

header {
  background-color: green;
  grid-column: 1;
  grid-row: 1
}

main {
  background-color: blue;
  grid-column: 1;
  grid-row: 2;
}

nav {
  background-color: pink;
  grid-column: 1;
  grid-row: 3;
  overflow: auto;
}

footer {
  background-color: teal;
  grid-column: 1;
  grid-row: 4;
}

@media only screen and (orientation: landscape) {
  .grid-container {
    grid-template-columns: 5fr 4fr;
    grid-template-rows: 1fr 1fr 1fr;
  }
  nav {
    grid-column: 2;
    grid-row: 1 / span 3;
  }
  footer {
    grid-row: 3;
  }
}
<div class="grid-container">
  <header>
    <h1>Logo</h1>
  </header>
  <main>
    <h1>content</h1>
  </main>
  <nav>
    <h1>Navigation<br><br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br>nav item<br></h1>
  </nav>
  <footer>
    <h1>Footer</h1>
  </footer>
</div>

revised codepen


Browser Support for CSS Grid

  • Chrome - full support as of March 8, 2017 (version 57)
  • Firefox - full support as of March 6, 2017 (version 52)
  • Safari - full support as of March 26, 2017 (version 10.1)
  • Edge - full support as of October 16, 2017 (version 16)
  • IE11 - no support for current spec; supports obsolete version

Here's the complete picture: http://caniuse.com/#search=grid

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • Thank you very much @Michael_B for your time. But this solution only works if the navigation is bigger then the left content. My Navigation just takes half the space of the left content. I am not sure if this really works well with grid layouts. – ruslansteiger May 10 '17 at 20:25
  • Nearly works. But if I scroll on the left side my navigation disappears. Is there a way to wrap the left content together? – ruslansteiger May 10 '17 at 21:15
  • 1
    If you can wrap the left elements in one container, then everything gets a lot easier. Just use flexbox. https://codepen.io/anon/pen/jmZgEo?editors=1100 – Michael Benjamin May 11 '17 at 01:41
  • 1
    Yeah thank you. That was my next idea to use flex. Looks like grid is not suited for my solution! Thank you very much for your time @Michael_B you helped alot ;) – ruslansteiger May 12 '17 at 07:13
4

Here is a extended version from my answer on your earlier question, how to get scroll for both header/content/main and nav using flexbox.

Fiddle demo

Stack snippet

(function(w, d, timeout) {
  w.addEventListener("load", function() {
    resizer();
  }, false);

  w.addEventListener("resize", function() {
    if (!timeout) {
      timeout = setTimeout(function() {
        timeout = null;
        // do resize stuff
        resizer();
      }, 66);
    }
  }, false);

  function resizer() {
    if (w.innerHeight < w.innerWidth) {
      if (!(d.body.classList.contains('landscape'))) {
        d.body.classList.add('landscape');
        d.body.appendChild(d.querySelector('nav'));
      }
    } else {
      if (d.body.classList.contains('landscape')) {
        d.body.classList.remove('landscape')
        d.querySelector('section').appendChild(d.querySelector('nav'));
      }
    }
  }
}(window, document));
html, body {
  margin:0;
}
header, footer, main, nav {
  margin: 5px;
  padding: 5px;
  border-radius: 5px;
  min-height: 120px;
  border: 1px solid #eebb55;
  background: #ffeebb;
}
footer {
  order: 2;
}
nav {
  order: 1;
}
section {
  display: flex;
  flex-direction: column;
}

@media only screen and (orientation: landscape) {

  main div {
    height: 400px;
    border: 1px dashed red;
  }
  nav div {
    height: 800px;
    border: 1px dashed red;
  }

  body.landscape {
    display: flex;
  }
  section {
    display: block;
    width: calc(60% - 10px);         /*  10px is for the margin  */
    box-sizing: border-box;
    max-height: calc(100vh - 20px);
    overflow: auto;
  }
  nav {
    width: calc(40% - 10px);         /*  10px is for the margin  */
    box-sizing: border-box;
    max-height: calc(100vh - 20px);
    overflow: auto;
  }
}
<section>
  <header>header</header>
  <main>content
    <div>
      This div get a height when in landscape to show scroll in section
    </div>
  </main>
  <footer>footer</footer>
  <nav>navigation
    <div>
      This div get a height when in landscape to show scroll in nav
    </div>
  </nav>
</section>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • Yeah nice job @LGSon. This works perfect. Could I ask you some basic question. I am not that skilled in javascript. What does the first and last part actually do? "(function(d, timeout) {" and "}(document));" You are using the variable d always to manipulate the dom but how is it defined? I don't understand the syntax. And I don't get the timeout variable. Looks like magic to me. The code does make sense how you move the nav part around the tree. – ruslansteiger May 17 '17 at 18:26
  • 1
    @SuddenlyRust The `(function(d, timeout) {...})();` calls a closure and wrap what's in it so it does not pollute the global scope with variables and stuff. It executes itself with the last `(document)` and where `document` (the DOM's document) is passed in as the first parameter `d`. I did this so instead of writing `document.body` etc, I can use the shorter `d.body`. The `timeout` is used in the resize event and since it is passed in like that, as a parameter, it is accessible through out the entire closure function, just like a global variable is. Hope this make sense. – Asons May 17 '17 at 18:39
  • yeah wow thanks. so much to learn ;) really appreatice your time. have a great day mate. I am gonna rewrite it in jQuery haha. so I can understand it much easier. But I think I can use your solution. it works perfectly fine – ruslansteiger May 17 '17 at 18:59
  • 1
    @SuddenlyRust I recommend to not rewrite the resize handler part and use jQuery resize event, because it does not have the _throttler_ functionality mine has, unless you add the equivalent plug-in of course. What I provided with plain javascript has the less impact as possible on performance and there is no real reason to _jQuery_-fy it. – Asons May 17 '17 at 19:18
  • probably I could just remove jQuery in my current project and try to rewrite everything in plain javascript. How would you rewrite following in plain JS? https://codepen.io/SuddenlyRust/pen/JNaygY?editors=0010# It would be a bit complicated or? Not sure how to add the 'one' handler with animationend – ruslansteiger May 17 '17 at 19:42
  • 1
    @SuddenlyRust With something like this: https://codepen.io/anon/pen/PmdJGR ... though it is perfectly fine to use both jQuery and plain javascript. About animation events, here is a good article: https://www.sitepoint.com/css3-animation-javascript-event-handlers/ – Asons May 17 '17 at 19:56