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

5

15

"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.

Physiography answered 17/12, 2016 at 13:15 Comment(5)
If you know the current element you are in you can know the position of that element inside it's parent (and the parent [row] inside his parent [grid-container]).Synod
Could you prepare a fiddle so that we can see how complex your markup is?Spectrogram
codepen.io/devrafalko/pen/LbMgmo run console.logMisdoing
Thanks @Pawel, however I'm trying to get the current cell that the mouse is in not the element. How do I work out the grid coordinates for the cell with no element in it. A similar issue applies to the spanned element going across two cells. The only solution I can see is if I put "hidden elements" in each grid cell and target that however this feels a bit clunky and isn't very dynamic.Physiography
This is a very interesting question, but I suspect there will be no simple answer. There's nothing representing the individual grid areas in the DOM (only elements that can be placed in areas - but even then, they may not cover the whole area), so there's no source of knowledge for the event information. The only possibility for a generalized solution as I see it would be to populate and render the grid with the desired items, reverse engineer the grid lines and their respective position in relation to the content, and then determine the location based on that. Not easy, nor performant.Pygmy
R
3

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.

Roadside answered 17/12, 2016 at 13:42 Comment(1)
Hi @partizanos, thanks for your answer :) I've added a codepen above. I'm not sure this quite works though. Doesn't this assume that there is an element within each grid cell? What if one of the cells is empty? Also what if an element spans across more than one cell?Physiography
K
2

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
});
Kissinger answered 17/12, 2016 at 15:5 Comment(1)
Hi @Pawel, Thanks for your answer :) This would work great for grids of equal sizes however css grids enables you to have columns and rows of different heights and widths. My current example shows columns of various widths: grid-template-columns: 25% 50% 25%;.Physiography
I
1

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

Incommensurate answered 8/11, 2021 at 1:28 Comment(0)
M
0

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.

Millennium answered 3/8, 2017 at 9:57 Comment(0)
T
0

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.

Tonedeaf answered 12/1, 2018 at 3:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.