skip to Main Content

The goal is to set the style and get the bounding box of a whole "row" in a css grid.
(Style setting such as highlight when hovering).

For setting the style there is the specific feature to use display: contents so the styles can affect the child elements without influencing the tree.

However I notice that if I then try to get the size (height) of that row the getBoundingClientRect returns all zeros.

const row = document.getElementById("firstrow");
console.log(row.getBoundingClientRect());

const actual_cell = document.getElementById("data");
console.log(actual_cell.getBoundingClientRect());
.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
.row {
  display: contents;
}
.row:hover div {
  background-color: lightgray;
}
.cell {
  border: 1px black solid;
  max-height: 100px;
}
.ipsum {
  height: 200px;
  max-height: 300px;
}
<div class="grid">
  <div class="row" id="firstrow">
      <div class="cell">hello</div>
      <div class="cell" id="data">world</div>
      <div class="cell ipsum">ipsum lorem</div>
  </div>
</div>

Of course I could iterate everything "below" the row. However this is quite a complex way, as not only getting the max height of the element, one has to check how it is displayed (pop up menus, or nested display:contents etc) and where (maybe the row is actually spanning multiple rows and there are 4 cells in above example in a "row").

So can this be done in an easy way?

3

Answers


  1. You could set the style of the row with JavaScript to match that of your grid; get your values; and, then remove the inline styles to reset the row to display: contents.

    const grid = document.querySelector('.grid');
    console.log('grid:', grid.getBoundingClientRect());
    
    const row = document.getElementById("firstrow");
    row.style.gridTemplateColumns = '1fr 1fr 1fr'
    row.style.display = 'grid';
    console.log('row height:', row.offsetHeight);
    console.log('row top:', row.offsetTop);
    row.style = ''
    
    const actual_cell = document.getElementById("data");
    console.log('ipsum cell:', actual_cell.getBoundingClientRect());
    .grid {
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
    }
    .row {
      display: contents;
    }
    .row:hover div {
      background-color: lightgray;
    }
    .cell {
      border: 1px black solid;
      max-height: 100px;
    }
    .ipsum {
      height: 200px;
      max-height: 300px;
    }
    
    * {
      margin: 0;
      box-sizing: border-box;
    }
    <h1>hello world</h1>
    <div class="grid">
      <div class="row" id="firstrow">
          <div class="cell">hello</div>
          <div class="cell">world</div>
          <div class="cell ipsum" id="data">ipsum lorem</div>
      </div>
    </div>
    Login or Signup to reply.
  2. Yes, Elements with display: contents feel weird as if they are and are not in the DOM at the same time.

    Getting the bounds and covering the row with a red div:

    const row = document.querySelectorAll("#firstrow > *");
    
    const rect = [...row]
      .map(item => item.getBoundingClientRect())
      .reduce((a, b) => {
        const x = Math.min(a.left, b.left), 
          y = Math.min(a.top, b.top),
          width = Math.max(a.right, b.right) - x,
          height = Math.max(a.bottom, b.bottom) - y;
    
        return new DOMRectReadOnly(x, y, width, height);
      });
    
    console.log(rect);
    
    var cover = document.createElement("div");
    document.body.append(cover);
    
    Object.assign(cover.style, {
      position: "absolute",
      top: rect.y + "px",
      left: rect.x + "px",
      width: rect.width + "px",
      height: rect.height + "px",
      background: "#FF0000A0"
    });
    .grid {
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
    }
    .row {
      display: contents;
    }
    .row:hover div {
      background-color: lightgray;
    }
    .cell {
      border: 1px black solid;
      max-height: 100px;
    }
    .ipsum {
      height: 200px;
      max-height: 300px;
    }
    <div class="grid">
      <div class="row" id="firstrow">
          <div class="cell">hello</div>
          <div class="cell" id="data">world</div>
          <div class="cell ipsum">ipsum lorem</div>
      </div>
    </div>
    Login or Signup to reply.
  3. It seems that you can get the height of one row in px through the computed value of the grid-template-rows property of the grid element.
    This computed value will return a list of all the rows’s computed height, you can then grab the one you’re interested in by splitting the resulting string and getting the value at the index you wish:

    const big_cell = document.querySelector(".ipsum");
    console.log("big_cell:", big_cell.getBoundingClientRect().height);
    const small_cell = document.querySelector(".small");
    console.log("small_cell:", small_cell.getBoundingClientRect().height);
    
    const grid = document.querySelector(".grid");
    document.querySelectorAll(".row").forEach((row, index) => {
      row.addEventListener("mouseenter", (evt) => {
        const computed = getComputedStyle(grid).gridTemplateRows.split("px")[index];
        console.log("current-row", computed);
      });
    });
    .grid {
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
    }
    .row {
      display: contents;
    }
    .row:hover div {
      background-color: lightgray;
    }
    .cell {
      border: 1px black solid;
      max-height: 100px;
    }
    .ipsum {
      height: 200px;
      max-height: 300px;
    }
    <div class="grid">
      <div class="row">
          <div class="cell">hello</div>
          <div class="cell">world</div>
          <div class="cell ipsum">ipsum lorem</div>
      </div>
      <div class="row">
          <div class="cell small">hello</div>
          <div class="cell">world</div>
          <div class="cell">ipsum lorem</div>
      </div>
      <div class="row">
          <div class="cell">hello</div>
          <div class="cell">world</div>
          <div class="cell ipsum">ipsum lorem</div>
      </div>
      <div class="row">
          <div class="cell">hello</div>
          <div class="cell">world</div>
          <div class="cell">ipsum lorem</div>
      </div>
    
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search