Rockster160
Rockster160

Reputation: 1648

Make grid item consume the space of another item that has been removed

Essentially what I need to happen is to set up a grid, but if one of the elements is missing, another element stretches to fill the space.

This is an example Pen of where I'm currently at:

https://codepen.io/Rockster160/pen/JMLaXY

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

div {
  box-sizing: border-box;
}

.grid {
  height: 100%;
  width: 100%;
  background: white;
  border: 2px solid red;
  display: grid;
  grid-template-columns: 250px auto;
  grid-template-rows: 100px auto 50px;
  grid-template-areas: "sidebar header" 
                       "sidebar content"
                       "sidebar footer";
}

.sidebar {
  grid-area: sidebar;
  background: green;
}

.header {
  grid-area: header;
  background: lightblue;
}

.content {
  grid-area: content;
  background: blue;
  border: 5px solid black;
}

.footer {
  grid-area: footer;
  background: orange;
}
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <!--   <div class="footer"></div> -->
</div>

footer is an optional element, so when it doesn't exist (commented out as in the code) then content should stretch and line up with the bottom of sidebar.

I've tried a variety of different combinations using min/max content and different auto placements, but no luck. I thought if I had multiple elements named content it might work as well, but no luck there either.

Any help is greatly appreciated!

Upvotes: 1

Views: 91

Answers (4)

vals
vals

Reputation: 64164

You are forcing the 3rd row to be 50px in the grid style.

Change it to be adapted to the content, and set the 50px as height in the footer itself:

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

div {
  box-sizing: border-box;
}

.grid {
  height: 100%;
  width: 100%;
  background: white;
  border: 2px solid red;
  display: grid;
  grid-template-columns: 250px auto;
  grid-template-rows: 100px auto max-content;   /* changed last 50px to max-content*/
  grid-template-areas: "sidebar header" 
                       "sidebar content"
                       "sidebar footer";
}

.sidebar {
  grid-area: sidebar;
  background: green;
}

.header {
  grid-area: header;
  background: lightblue;
}

.content {
  grid-area: content;
  background: blue;
  border: 5px solid black;
}

.footer {
  grid-area: footer;
  background: orange;
  height: 50px;   /* added */
}
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <!--   <div class="footer"></div> -->
</div>

And another posibility, thanks to Michael_B. The sintax of grid-template-rows is clearer:

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

div {
  box-sizing: border-box;
}

.grid {
  height: 100%;
  width: 100%;
  background: white;
  border: 2px solid red;
  display: grid;
  grid-template-columns: 250px auto;
  grid-template-rows: 100px 1fr auto;   
  grid-template-areas: "sidebar header" 
                       "sidebar content"
                       "sidebar footer";
}

.sidebar {
  grid-area: sidebar;
  background: green;
}

.header {
  grid-area: header;
  background: lightblue;
}

.content {
  grid-area: content;
  background: blue;
  border: 5px solid black;
}

.footer {
  grid-area: footer;
  background: orange;
  height: 50px;   /* added */
}
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <div class="footer"></div>
</div>

Upvotes: 2

Michael Benjamin
Michael Benjamin

Reputation: 371113

Sometimes things are simpler with flexbox.

Since your container has a defined height (the viewport), you can use flex-flow: column wrap to create both columns.

Then use flex: 1 on the content item, which tells it to consume free space.

When the footer is present, the content makes space for it. When the footer is not present, the content consumes all space.

.grid {
  display: flex;
  flex-flow: column wrap;
  height: 100vh;
  background: white;
  border: 2px solid red;
}

.sidebar {
  flex: 0 0 100%;
  width: 250px;
  background: green;
}

.header {
  flex: 0 0 100px;
  width: calc(100% - 250px);
  background: lightblue;
}

.content {
  flex: 1;
  width: calc(100% - 250px);
  border: 5px solid black;
  background: blue;
}

.footer {
  flex: 0 0 50px;
  width: calc(100% - 250px);
  background: orange;
}

body { margin: 0; }
div  { box-sizing: border-box; }
<!-- WITH FOOTER -->
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <div class="footer"></div>
</div>

<hr>

<!-- WITHOUT FOOTER -->
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
</div>

Upvotes: 0

David Thomas
David Thomas

Reputation: 253308

The easiest way to do this is to use the :last-child selector:

.content:last-child {
  grid-row: content / footer;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

div {
  box-sizing: border-box;
}

.grid {
  height: 100%;
  width: 100%;
  background: white;
  border: 2px solid red;
  display: grid;
  grid-template-columns: 250px auto;
  grid-template-rows: 100px auto 50px;
  grid-template-areas: "sidebar header" "sidebar content" "sidebar footer";
  margin-bottom: 2rem;
}

.sidebar {
  grid-area: sidebar;
  background: green;
}

.header {
  grid-area: header;
  background: lightblue;
}

.content {
  grid-area: content;
  background: blue;
  border: 5px solid black;
}

.content:last-child {
  grid-row: content / footer;
}

.footer {
  grid-area: footer;
  background: orange;
}
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <!-- <div class="footer"></div> -->
</div>

<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <div class="footer"></div>
</div>

Or, alternatively, we could reverse the order of the .content and .footer elements in the HTML (as below) and use the CSS negation operator (:not()) to determine that the .content element should take up extra space if it is not preceded by a .footer element:

:not(.footer) + .content {
  grid-row: content/footer;
}

which styles a .content element that is not immediately preceded by a .footer sibling in such a way that it starts in the grid-row identified by content and ends in the grid-row identified by footer:

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

div {
  box-sizing: border-box;
}

.grid {
  height: 100%;
  width: 100%;
  background: white;
  border: 2px solid red;
  display: grid;
  grid-template-columns: 250px auto;
  grid-template-rows: 100px auto 50px;
  grid-template-areas: "sidebar header" "sidebar content" "sidebar footer";
}

.sidebar {
  grid-area: sidebar;
  background: green;
}

.header {
  grid-area: header;
  background: lightblue;
}

.content {
  grid-area: content;
  background: blue;
  border: 5px solid black;
}

:not(.footer)+.content {
  grid-row: content/footer;
}

.footer {
  grid-area: footer;
  background: orange;
}
<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <!-- <div class="footer"></div> -->
</div>

<div class="grid">
  <div class="sidebar"></div>
  <div class="header"></div>
  <div class="content"></div>
  <div class="footer"></div>
</div>

References:

Upvotes: 1

Wagner Moreira
Wagner Moreira

Reputation: 1088

Change your .grid class to

  .grid {
    display: grid;
    grid-template-columns: 250px auto;
    grid-template-rows: 100px auto 50px;
    grid-template-areas:
      "sidebar header"
      "sidebar content"
      "sidebar content";
  }

when you comment your footer tag as in your pen, grid is still waiting to have a footer element there, so it's kinda "saving space" for this element there

Upvotes: -1

Related Questions