如何使最后一个网格项填充网格剩余空间?

时间:2018-06-22 14:53:33

标签: html css-grid

这是一个简单的网格:https://codepen.io/ChucKN0risK/pen/zaWQOm

HTML:

<div class="card-wrapper">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="space-filler">Space filler</div>
</div>

CSS:

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

.card-wrapper {
  display: grid;
  grid-gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  width: 100%;
  height: 100%;
}

.card {
  background-color: royalblue;
}

.space-filler {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: tomato;
}

我希望我的“空格填充”项填充网格的剩余空间,但仅当最后一行具有多个网格项时才填充。

这有可能吗?

预先感谢,祝您愉快

1 个答案:

答案 0 :(得分:0)

我确实设法使用JS创建了自己的解决方案

代码如下:https://codepen.io/ChucKN0risK/pen/zaWQOm

HTML:

<div class="card-wrapper js-grid-wrapper">
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="card js-grid-el"></div>
  <div class="space-filler is-hidden js-space-filler">Space filler</div>
</div>

<div class="element-adder">
  <button class="js-add-el">+ Add element</button>
  <button class="js-remove-el">- Remove element</button>
</div>

SCSS:

.card-wrapper {
  position: relative;
  display: grid;
  grid-gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  width: 100%;
  height: 100%;
}

.card {
  background-color: royalblue;
}

.space-filler {
  position: absolute;
  bottom: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: tomato;
  opacity: 0.8;

  &.is-hidden {
    display: none;
  }
}

// -------------------------
// DEMO SPECIFIC STYLE
// -------------------------

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

.element-adder {
  display: flex;
  flex-direction: column;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 2rem;

  button {
    flex: 1;
    padding: 0.5rem;
    border: none;
    background-color: white;
    cursor: pointer;
  }

  button + button {
    margin-top: 0.5rem;
  }
}

JS:

class SpaceFillerElement {
  constructor() {
    this._gridWrapper = document.querySelector('.js-grid-wrapper');
    this.gridElClassname = 'js-grid-el';
    this._el = this._gridWrapper.querySelector('.js-space-filler');
    this._gridEl = document.querySelector(`.${this.gridElClassname}`);
    this.gridElCount = this._gridWrapper.querySelectorAll(`.${this.gridElClassname}`).length;
    this.gridColumnGap = getComputedStyle(this._gridWrapper).gridColumnGap;
    this.events();
  }

  events() {
    this.elementAdder();
    this.observeGridWrapper();
    this.displayEl();
    this.setElDimensions();
    // Since the user can resize
    // the viewport we need to listen to the resize event so that
    // the space filler element can adjust its dimensions.
    window.addEventListener('resize', (e) => {
      this.getColumnsCount();
      this.setElDimensions();
      this.displayEl();
    });  
  }

  getColumnsCount() {
    const gridWrapperWidth = this._gridWrapper.clientWidth;
    const gridElWidth = document.querySelector(`.${this.gridElClassname}`).clientWidth;
    return Math.floor(gridWrapperWidth / gridElWidth);
  }

  displayEl() {
    this.gridElCount = this._gridWrapper.querySelectorAll(`.${this.gridElClassname}`).length;
    // If Number of elements / number of columns doesn't
    // return an integer, it means that elements will be
    // in the last row. And our space filler only displays
    // itself during that condition.
    if (this.gridElCount % this.getColumnsCount() !== 0) {
      this._el.classList.remove('is-hidden');
    } else {
      this._el.classList.add('is-hidden');
    }
  }

  setElDimensions() {
    // 1) this._gridEl is the first child of our grid container. However,
    // everytime we remove an item, we remove the first child which makes
    // this._gridEl.clientHeight returns 0. That's why we must reassign
    // its value to the new first child of our grid container.
    this._gridEl = document.querySelector(`.${this.gridElClassname}`);
    // 2) We must know the number of grid item to determine the
    // number of child present in the last row.
    this.gridElCount = this._gridWrapper.querySelectorAll(`.${this.gridElClassname}`).length;
    const ElOnLastRow = this.gridElCount % this.getColumnsCount();
    // 3) We set our space filler element's dimensions according to the
    // space available in the last row.
    const ElWidth = `calc(${this._gridEl.clientWidth}px * (${this.getColumnsCount()} - ${ElOnLastRow}) + (${this.gridColumnGap} * (${this.getColumnsCount()} - ${ElOnLastRow})))`;
    const ElHeight = `calc(${this._gridEl.clientHeight}px + ${this.gridColumnGap})`;
    this._el.style.height = ElHeight;
    this._el.style.width = ElWidth;
  }

  // In my case, my grid item are dynamic and I need to adjust
  // the space filler dimensions according to the number of
  // grid items in the grid. I use the MutationObserver
  // to call the this.displayEl() and this.setElDimensions() methods
  // when child elements are added or removed from the grid.
  observeGridWrapper() {
    // Options for the observer (which mutations to observe)
    const config = {
      attributes: false,
      childList: true,
      subtree: true,
    };

    // Callback function to execute when mutations are observed
    const callback = (mutationsList) => {
      for(const mutation of mutationsList) {
        if (mutation.type == 'childList') {
          console.log('A child node has been added or removed.');
          this.displayEl();
          this.setElDimensions();
        }
      }
    };

    // Create an observer instance linked to the callback function
    const observer = new MutationObserver(callback);

    // Start observing the target node for configured mutations
    observer.observe(this._gridWrapper, config);
  }

  // I use this to simulate the adding or the removal of grid items.
  elementAdder() {
    document.querySelector('.js-add-el').addEventListener('click', () => {
      // Create a new element
      const newNode = document.createElement('div');
      newNode.classList.add('card');
      newNode.classList.add(this.gridElClassname);
      // Insert the new node after the last element in the parent node
      this._gridWrapper.append(newNode);
    });
    document.querySelector('.js-remove-el').addEventListener('click', (e) => {
      this._gridWrapper.querySelector(`.${this.gridElClassname}`).remove();
    });
  }
}

new SpaceFillerElement();