最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

css - Properly size vertically-scrollable list on a non-scrollable page - Stack Overflow

programmeradmin1浏览0评论

I want to build a non-scrolling web page (a "dashboard"), for use on desktop monitors or large tablets. It will have two sections: an area for images that have been scaled to fit the available space, and a list of descriptions of recent updates. Each description will be short and will be able to fit on one line.

Since the size of the list can vary considerably, I want to present it in a container with the following properties:

  1. The container's width should adjust to the width of the longest description.
  2. The container's height should adjust to display as many rows as it can without extending beyond the bottom of the page or truncating the data.
  3. If the list has more rows than will fit in the available vertical space on the page, the container should extend to the bottom of the page and acquire a vertical scrollbar. (It should "become scrollable".)
  4. If the list can fit entirely in the available vertical space, the container doesn't need to be scrollable and should not display a scroll gutter.

Here is a mockup illustrating the two basic conditions and the desired appearance:

I've posted a snippet demonstrating my solution (please run it as a full page).

As it stands, the container has a fixed height of 300px, since I don't know how to make it dynamic.

I'm open to a complete redesign if advised, but I want to limit myself to HTML, CSS, and JS.

/* This function populates the scrollable-container grid with variable
     numbers of rows of dummy data */
let my_limit;

function load_scrollable_table(limit) {
  let perday = 3;
  my_limit = limit;
  const my_div = document.querySelector("#table-insertion-point");
  for (let day = 1;; day++) {
    if (!insert_row(my_div, `3/${day}/2025`, 'Foobar etc.')) {
      return;
    }
    for (let thisday = 1; thisday < perday; ++thisday) {
      let v = "foo ".repeat(thisday * 4);
      if (!insert_row(my_div, '', 'Foobar more etc.' + v)) {
        return;
      }
    }
  }
}

function insert_row(my_div, col1, col2) {
  let my_row = `<div class="scrollable-table-col1">${col1}</div><div class="scrollable-table-col2">${col2}</div>`;
  my_div.innerHTML += my_row;
  return --my_limit;
}
* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  /* height/width are that of viewport */
  width: 100%;
  background: lightgray;
  margin: 0;
  padding: 0;
}

.main-div {
  display: flex;
  flex-flow: column;
  height: 100%;
  /* set height to that of body */
  margin: 0 1em;
  /* inset main-div from body */
}

.hdr-div {
  text-align: center;
}

.lr-split-div {
  display: flex;
  flex-flow: row;
  width: 100%;
  height: 100%;
}

/**** LEFT SIDE ****/
.left-side {
  flex: 50;
  display: flex;
  flex-flow: column;
  /* flow downwards */
  background: pink;
}

/**** Leftside content ****/
.left-top-div {
  margin-bottom: 15px;
  /* Gap beneath Welcome blurb */
}

/* Container for scrollable content -- height should be tall
       enough to accommodate content, but should not extend beyond
       bottom of window and become scrollable if so. */

.scrollable-container {
  width: fit-content;
  height: 300px;
  /* we want this to be dynamic */
  padding: 10px;
  border: 4px solid #888;
}

/* Inner content that will scroll */
.scrollable-content {
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-columns: auto auto;
  column-gap: 10px;
  overflow-y: scroll;
  padding: 5px 10px;
}

.scrollable-table-col1 {}

.scrollable-table-col2 {
  white-space: nowrap;
  /* dont wrap text */
}

/**** RIGHT SIDE FOR SLIDESHOW ****/
.right-side {
  flex: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: cyan;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

<!-- Number of rows = 20. Change as needed. -->
<body onload="load_scrollable_table(20)">
  <div class="main-div">
    <div class="hdr-div">
      <h1>This is the page header with logo etc.</h1>
    </div>
    <div class="lr-split-div">

      <!-- ***** LEFT SIDE ***** -->

      <div class="left-side">
        <div class="left-top-div">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
          ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div> <!-- END welcome -->

        <hr />

        <h2>Recent site updates:</h2>
        <div class="scrollable-container">
          <div id="table-insertion-point" class="scrollable-content">
            <!-- Javascript inserts table here -->
          </div> <!-- END scrollable-content -->
        </div> <!-- END scrollable-container -->
      </div> <!-- END left-side -->

      <!-- ***** RIGHT SIDE ***** -->
      <div class="right-side">
        Slideshow
      </div> <!-- END right-side -->
    </div> <!-- END split div -->
  </div> <!-- END of outer div -->
</body>

</html>

I want to build a non-scrolling web page (a "dashboard"), for use on desktop monitors or large tablets. It will have two sections: an area for images that have been scaled to fit the available space, and a list of descriptions of recent updates. Each description will be short and will be able to fit on one line.

Since the size of the list can vary considerably, I want to present it in a container with the following properties:

  1. The container's width should adjust to the width of the longest description.
  2. The container's height should adjust to display as many rows as it can without extending beyond the bottom of the page or truncating the data.
  3. If the list has more rows than will fit in the available vertical space on the page, the container should extend to the bottom of the page and acquire a vertical scrollbar. (It should "become scrollable".)
  4. If the list can fit entirely in the available vertical space, the container doesn't need to be scrollable and should not display a scroll gutter.

Here is a mockup illustrating the two basic conditions and the desired appearance:

I've posted a snippet demonstrating my solution (please run it as a full page).

As it stands, the container has a fixed height of 300px, since I don't know how to make it dynamic.

I'm open to a complete redesign if advised, but I want to limit myself to HTML, CSS, and JS.

/* This function populates the scrollable-container grid with variable
     numbers of rows of dummy data */
let my_limit;

function load_scrollable_table(limit) {
  let perday = 3;
  my_limit = limit;
  const my_div = document.querySelector("#table-insertion-point");
  for (let day = 1;; day++) {
    if (!insert_row(my_div, `3/${day}/2025`, 'Foobar etc.')) {
      return;
    }
    for (let thisday = 1; thisday < perday; ++thisday) {
      let v = "foo ".repeat(thisday * 4);
      if (!insert_row(my_div, '', 'Foobar more etc.' + v)) {
        return;
      }
    }
  }
}

function insert_row(my_div, col1, col2) {
  let my_row = `<div class="scrollable-table-col1">${col1}</div><div class="scrollable-table-col2">${col2}</div>`;
  my_div.innerHTML += my_row;
  return --my_limit;
}
* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  /* height/width are that of viewport */
  width: 100%;
  background: lightgray;
  margin: 0;
  padding: 0;
}

.main-div {
  display: flex;
  flex-flow: column;
  height: 100%;
  /* set height to that of body */
  margin: 0 1em;
  /* inset main-div from body */
}

.hdr-div {
  text-align: center;
}

.lr-split-div {
  display: flex;
  flex-flow: row;
  width: 100%;
  height: 100%;
}

/**** LEFT SIDE ****/
.left-side {
  flex: 50;
  display: flex;
  flex-flow: column;
  /* flow downwards */
  background: pink;
}

/**** Leftside content ****/
.left-top-div {
  margin-bottom: 15px;
  /* Gap beneath Welcome blurb */
}

/* Container for scrollable content -- height should be tall
       enough to accommodate content, but should not extend beyond
       bottom of window and become scrollable if so. */

.scrollable-container {
  width: fit-content;
  height: 300px;
  /* we want this to be dynamic */
  padding: 10px;
  border: 4px solid #888;
}

/* Inner content that will scroll */
.scrollable-content {
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-columns: auto auto;
  column-gap: 10px;
  overflow-y: scroll;
  padding: 5px 10px;
}

.scrollable-table-col1 {}

.scrollable-table-col2 {
  white-space: nowrap;
  /* dont wrap text */
}

/**** RIGHT SIDE FOR SLIDESHOW ****/
.right-side {
  flex: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: cyan;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

<!-- Number of rows = 20. Change as needed. -->
<body onload="load_scrollable_table(20)">
  <div class="main-div">
    <div class="hdr-div">
      <h1>This is the page header with logo etc.</h1>
    </div>
    <div class="lr-split-div">

      <!-- ***** LEFT SIDE ***** -->

      <div class="left-side">
        <div class="left-top-div">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
          ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div> <!-- END welcome -->

        <hr />

        <h2>Recent site updates:</h2>
        <div class="scrollable-container">
          <div id="table-insertion-point" class="scrollable-content">
            <!-- Javascript inserts table here -->
          </div> <!-- END scrollable-content -->
        </div> <!-- END scrollable-container -->
      </div> <!-- END left-side -->

      <!-- ***** RIGHT SIDE ***** -->
      <div class="right-side">
        Slideshow
      </div> <!-- END right-side -->
    </div> <!-- END split div -->
  </div> <!-- END of outer div -->
</body>

</html>

Share Improve this question edited yesterday Chap asked Feb 14 at 17:56 ChapChap 3,8372 gold badges52 silver badges91 bronze badges 3
  • Are you surprised that the scroll bar in a browser can have different widths or positions? What is the essence of the question? – imhvost Commented Feb 14 at 19:38
  • I am interested in the problem, but not really from the perspective of the technicalities of Firefox scrollbar behaviour. I'm interested in your design; in what you are trying to achieve and whether you have taken the right tack with your layout. But your post doesn't give me enough to go on here. My suggestion would be to add a couple of paragraphs to the beginning of your post which provide the big picture: an overview of the problem you are solving, the list of required features, and a description of your design without any references to implementation specifics (HTML and CSS). – Brett Donald Commented Feb 18 at 0:23
  • @BrettDonald - thanks for your suggestions. I have rewritten question, hopefully addressing your ideas. I didn't know how to describe my design without reference to HTML/CSS. Hope it's clear what I'm trying to do. – Chap Commented Feb 19 at 23:35
Add a comment  | 

1 Answer 1

Reset to default 1

I would use grids rather than flexboxes to lay out a dashboard. The way I like to think about it is that with flexboxes, the content controls the layout, whereas with grids, the layout controls the content. The key feature of grids which will help you is the ability to divide up vertical space by specifying a row template using grid-template-rows. You can allow one row to take as much space as it needs, then have a second row which just gets whatever space is left over. That’s exactly what you want to do.

First, let’s tidy up your HTML. Did you know that instead of using <div> (which is just a generic element typically used for grouping) you can invent a custom tag name which makes its purpose clear? This is the document structure and tag names I would suggest:

<body>
  <header></header>
  <panels>
    <left-panel>
      <left-panel-top>
        <welcome></welcome>
        <hr>
        <h2>Recent site updates:</h2>
      </left-panel-top>
      <left-panel-bottom>
        <left-panel-bottom-content>
        </left-panel-bottom-content>
      </left-panel-bottom>
    </left-panel>
    <right-panel>
      Slideshow
    </right-panel>
  </panels>
</body>

When it comes to styling, the first task is to specify how to divide up the vertical space in the body. For this, use grid-template-rows.

  • The header should be as big as it needs to be, so make the first row auto.
  • Let’s use an empty third row to implement a bottom margin, so make that 1em.
  • The 50/50 side-by-side panels should take all the remaining space, so make the middle row 1fr.

The CSS:

body {
  display: grid;
  grid-template-rows: auto 1fr 1em;  
}

Use a second grid to style the 50/50 side-by-side panels.

panels {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

Use a third grid to assign the vertical space in left-panel:

  • The items which comprise left-panel-top (the welcome, hr and h2) should be as big as they need to be, so make the first row auto.
  • The scrollable container, left-panel-bottom, should take all the remaining space, so make the second row 1fr.

The CSS:

left-panel {
  display: grid;
  grid-template-rows: auto 1fr;
}

The final puzzle piece is that you need to absolutely position the .scrollable-content element, which I have renamed as left-panel-bottom-content. This prevents the length of its content from affecting the layout.

Here is the finished result. After running it, you do need to use the full page link ... the small preview panel cannot accommodate the layout properly.

* {
  box-sizing: border-box;
}

html, body {
  height: 100%;
}

body {
  background: #f2f2f2;
  margin: 0 1em;            /* side margins only */
  overflow: hidden;         /* prevents any body scroll -- good for dashboards */
  display: grid;
/*
  GRID TEMPLATE ROWS
  - first row is auto -- this allows the header to take as much space as it needs
  - last row is 1em -- we will leave this empty to simulate a bottom margin
  - middle row is 1fr -- whatever space is left over 
*/  
  grid-template-rows: auto 1fr 1em;  
}

header {
  text-align: center;
}

panels {
  display: grid;
/*
  GRID TEMPLATE COLUMNS
  - two equal width columns
*/
  grid-template-columns: 1fr 1fr;
  gap: 1em;                   /* with a gap between */
}

left-panel {
  display: grid;
/*
  GRID TEMPLATE ROWS
  - first row is auto -- this allows the welcome, the hr and the h2 to take as much space as they need
  - last row is 1fr -- whatever space is left over 
*/  
  grid-template-rows: auto 1fr;
  background: pink;
}

hr {
  display: block;
  margin: 1em auto;
  width: 80%;
  border: 0;
  border-top: 1px dashed black;
}

left-panel-bottom {
  background: tan;
  position: relative;
}

left-panel-bottom-content {
  position: absolute;             /* prevents the height of the content from affecting the layout*/
  max-width: 100%;
  max-height: 100%;
  overflow: auto;                 /* scroll the overflow */
  border: 3px solid purple;
  padding: 0 1em;
}

right-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  background: cyan;
}
<header>
  <h1>This is the page header with logo etc.</h1>
</header>

<panels>

  <left-panel>
  
    <left-panel-top>
    
      <welcome>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
        eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
        ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
        aliquip ex ea commodo consequat. Duis aute irure dolor in
        reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
        culpa qui officia deserunt mollit anim id est laborum.
      </welcome>

      <hr>

      <h2 style="margin-top: 0;">Recent site updates:</h2>

    </left-panel-top>

    <left-panel-bottom>
      <left-panel-bottom-content>
        <p>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
        </p>
        <p>
          Maecenas tempor nunc mauris, sit amet placerat tortor lobortis dapibus.
        </p>
        <p>
          Nam lectus eros, maximus ac magna vel, congue consequat eros.
        </p>
        <p>
          Fusce id pretium diam. Cras sit amet pharetra ante.
        </p>
        <p>
          Sed quis commodo quam, vel facilisis ipsum.
        </p>
        <p>
          Vestibulum sodales iaculis arcu, et fringilla nisi ullamcorper sed.
        </p>
        <p>
          Donec interdum sit amet est non accumsan.
        </p>
        <p>
          Donec non augue feugiat, fermentum nunc non, convallis est.
        </p>
        <p>
          Cras vel ligula nec odio faucibus ultricies.
        </p>
        <p>
          Sed vulputate tortor eget pretium convallis.
        </p>
        <p>
          Cras interdum elit eget mi porta suscipit. Morbi ut velit diam.
        </p>
        <p>
          Etiam finibus eros et efficitur rutrum.
        </p>
        <p>
          Quisque viverra metus ac eleifend imperdiet.
        </p>
        <p>
          Quisque pretium ut purus vitae tempus.
        </p>
        <p>
          Duis varius risus congue velit faucibus, sed interdum purus consectetur.
        </p>
      <!--
        <p>
          Cras volutpat velit non mi sagittis condimentum. Cras tempor aliquet turpis sed pretium. Nunc aliquet sodales turpis quis ultrices. Duis auctor accumsan enim, quis maximus ex malesuada a. Donec a felis ut erat tempus euismod non vel neque. Proin lectus massa, sagittis at imperdiet nec, consequat ut neque. Sed vel placerat neque, vel varius urna. Vivamus interdum euismod urna a accumsan. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
        </p>
        <p>
          Nulla rhoncus aliquam mauris, eu pretium dolor auctor in. Maecenas a sollicitudin dolor, eget commodo quam. Proin et dui sed ligula vulputate egestas. Quisque eget augue vitae purus placerat pharetra. Aliquam rhoncus convallis lorem, sed facilisis odio blandit scelerisque. Vivamus viverra urna ac nulla interdum, eget ullamcorper leo maximus. Mauris nec feugiat enim. Nam congue, dui sit amet vestibulum posuere, leo mauris fermentum lorem, eget bibendum velit nunc quis leo.
        </p>
        <p>
          Curabitur eget ullamcorper justo, sit amet dictum neque. Fusce vitae ligula et felis auctor vulputate vel suscipit nibh. Integer a felis varius purus vestibulum viverra. Morbi venenatis placerat augue sit amet commodo. Sed dapibus molestie eros, vitae ultrices nunc commodo aliquam. Vivamus tempus mollis massa vel egestas. Donec ut ante quis eros commodo volutpat. Proin sem nisi, viverra ac sem tristique, consectetur laoreet sapien. Vivamus suscipit orci vel euismod scelerisque. Nullam sed pulvinar tellus. Nullam pulvinar arcu eget nibh rutrum, eget faucibus ligula ullamcorper.
        </p>
        <p>
          Sed sed cursus leo. Nam molestie eleifend leo, nec fermentum risus maximus ac. Pellentesque eget placerat ipsum. Vestibulum tempor quam justo. Fusce dapibus turpis non ante faucibus suscipit. Fusce rhoncus eleifend ipsum et lacinia. Curabitur nec congue arcu. Mauris dignissim magna ligula. Nullam ultrices, metus sit amet ultrices porttitor, turpis ligula interdum enim, eu pellentesque purus quam ut arcu. Nullam aliquet vitae tortor vel tincidunt. Fusce maximus lacus diam, sed elementum ligula condimentum vel. Sed consequat orci ac nunc gravida, at accumsan magna porttitor.
        </p>
        -->
      </left-panel-bottom-content>
    </left-panel-bottom>
  </left-panel>

  <right-panel>
    Slideshow
  </right-panel>
</panels>

It should be straight-forward for you to replace my placeholder text with your javascript which populates that section dynamically.

发布评论

评论列表(0)

  1. 暂无评论