CSS grid layout - how to work out which grid cell the mouse is in with javascript?

5.8k views Asked by At

"css grid layout" = https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout

Does anyone know of a javascript way to work out what column / row the mouse is currently in?

I'm guessing something like this is a start for getting the what cell the mouse is in however this would only be useful for grids of equal sizes.

MouseGridColumn = round(mouseX / ( gridContainerWidth  / gridTotalColumnsNumber ) )
MouseGridRow    = round(mouseY / ( gridContainerHeight / gridTotalRowsNumber    ) )

Is there a way to do this with cells that aren't equal?


Update 1 - adding a codepen

Here is a codepen which shows 3 various percentage column widths (grid-template-columns: 25% 50% 25%;), 2 rows, a grid cell with no element with in it and an element that spans more than one row: http://codepen.io/anon/pen/NbeOXp


Update 2 - attempt at working out what grid cell the mouse is in.

Here I have added hidden elements to each cell in the grid to detect which cell the mouse is in. However it only returns "auto" for the current element it is in. It's also pretty clunky having to add extra hidden elements to the grid IMO.

http://codepen.io/anon/pen/gLZBZw Hint: use the mouse to hover over the grid.

5

There are 5 answers

1
partizanos On

Add event listener to the parent of the grid colums. Its called event delegation. You can use event.target in on hover event and inside for loop compare the ev.target.parent element==document.querySelect("#parentElement")[index] Post all ur code html code to write it exactly if you want.

1
Paweł On

I think you're looking for solution for grids of different sizes, when the idea of grids is that they are always of the same size (totalWidth/gridNumber). Just the elements could then overflow 1,2,5 or more grids. I did a little object to detect the grid number change, if I misunderstood something please explain in the comment.

http://codepen.io/devrafalko/pen/bBOQbo?editors=1011

gridDetect = {
  gridNum:12,
  currentGrid:null,
  callback:null,
  initGridDetect: function(fun){
    var b = this.detectGrid.bind(this);
    document.addEventListener('mousemove',b);
    this.callback = fun;
  },
  detectGrid: function(event){
    var w = document.body.clientWidth;
    var mouse = event.clientX;
    var limitMouse = mouse > w ? w:mouse;
    var getGridNumber = Math.floor(limitMouse/(w/this.gridNum));
    if(this.currentGrid!==getGridNumber){
      this.currentGrid = getGridNumber;
      this.callback(getGridNumber);
    }
  }
}


gridDetect.initGridDetect(function(grid){
  console.log(grid);
  //it's called when the grid is about change
  //do what you want here with grid argument
});

0
Vadim Ovchinnikov On

You have two tasks here:

  1. Detecting element.
  2. Getting column / row of element.

Detecting element

You can avoid detecting element if you add needed event handler for every grid item.

But if you want to add mouse hover (or other event handler) you can add loop through every grid item and use element.getBoundingClientRect() method. Demo (I'll add handler for click to avoid polluting console):

var grid = document.querySelector(".grid");

grid.addEventListener("click", function(event) {
  var gridItems = this.children;
  for (var i = 0; i < gridItems.length; i++) {
    var gridItem = gridItems[i];
    var rect = gridItem.getBoundingClientRect();
    var elementDetected = event.clientX >= rect.left
     && event.clientX <= rect.right
      && event.clientY >= rect.top
      && event.clientY <= rect.bottom;
    if (elementDetected) {
      console.log(gridItem);
      return;
    }
  }
  
  console.log("no element detected!");
});
.grid {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  grid-gap: 10px;
}

/* styles just for demo */
.grid__item {
  background-color: tomato;
  color: white;
  
  /* styles for centering text */
  display: flex;
  align-items: center;
  justify-content: center;
}
<div class="grid">
  <div class="grid__item">One</div>
  <div class="grid__item">Two</div>
  <div class="grid__item">Three</div>
  <div class="grid__item">Four</div>
  <div class="grid__item">Five</div>
  <div class="grid__item">Six</div>
  <div class="grid__item">Seven</div>
  <div class="grid__item">Eight</div>
  <div class="grid__item">Nine</div>
</div>

Using jQuery would make this task even simplier due to is(":hover") method.

$(".grid").click(function(event) {
  var hoveredGridItems = $(this).children()
    .filter(function() { return $(this).is(":hover"); });
  
  if (hoveredGridItems.length > 0)
    console.log(hoveredGridItems[0]);
  else
    console.log("no element detected!");
});
.grid {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  grid-gap: 10px;
}

/* styles just for demo */
.grid__item {
  background-color: tomato;
  color: white;
  
  /* styles for centering text */
  display: flex;
  align-items: center;
  justify-content: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="grid">
  <div class="grid__item">One</div>
  <div class="grid__item">Two</div>
  <div class="grid__item">Three</div>
  <div class="grid__item">Four</div>
  <div class="grid__item">Five</div>
  <div class="grid__item">Six</div>
  <div class="grid__item">Seven</div>
  <div class="grid__item">Eight</div>
  <div class="grid__item">Nine</div>
</div>

Getting column / row of element

I would trust only CSS here, so getting column and row would work as expected only when we have explicitly set grid-column and grid-row property. So you have lines of code that will work in every case:

// pure JS
var styles = window.getComputedStyle(gritItem);
var gridColumn = styles["grid-column-start"];
var gridRow = styles["grid-row-start"];

// jquery
var gridColumn = $gridItem.css("grid-column");
var gridRow = $griditem.css("grid-row");

If value of gridColumn and/or gridRow is not auto / auto then you know row and column for sure.

But detecting row and column when auto-placement is very unreliable due to different possible column span / row span values, possible margins of grid items, align-self and justify-self (case when element can occupy only part of grid area) etc.

0
Victoria On

Add empty elements with a custom tag or class to top and left edge of grid, style them with "stretch", and then you add up the widths/heights to get the coordinates of the grid lines.

0
Gabriel España On

Here I use something that can help you, use .getComputedStyle, and take the already calculated value of the rows and columns (.gridTemplateRows and .gridTemplateColumns) of your grid, this saves it in an array and you can iterate with it as you like

https://codepen.io/leirbagabo/pen/MWoLYKz