I've tried and failed to use the nearly perfect answer given here: How to calculate the amount of flexbox items in a row?
The answer given returns the item count only in the top row. But I'm needing to get the position of an element clicked, in any row.
For example, in the below demo, if a user clicked on the box that is highlighted in black, how would I get the value of "2" out?
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 400px;
background-color: #ddd;
padding: 10px 0 0 10px;
margin-top: 5px;
overflow: auto;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}
<div id="grid" >
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>
I would assume a solution would not care about wrapping based on container size.
CodePudding user response:
This is one way of doing it:
document.getElementById("grid").addEventListener("click", function(event) {
const grid = Array.from(this.children);
const baseOffset = grid[0].offsetTop;
const breakIndex = grid.findIndex(item => item.offsetTop > baseOffset);
const numPerRow = (breakIndex === -1 ? grid.length : breakIndex);
const el = event.target;
const el_index = [...grid].indexOf(el);
const position = (el_index % numPerRow) 1
console.log(position);
});
CodePudding user response:
I have made explicit use of the rowLength
of each row, index
of each element to get the desired result. I have also removed the fixed width for .grid
container so that you can see for other width changes. You have to refresh the page to see it working for different width. For automatic size detection you may need to add resize event handler as well. I have also added comments on the script whenever necessary to help with the logic. You can run the code here itself and the position of the element will be alerted. Let me know if you need explanation on the script part. The solution will work for any number of rows and columns. I have wrapped the entire logic inside myFunc
just for readability regarding variable scope and all.
function myFunc() {
let items = document.getElementsByClassName("item");
let itemLength = items.length;
let rowLength = 1;
let baseOffsetTop = items[0].offsetTop;
// find length of a single row
for (let i = 1; i < itemLength; i ) {
if (baseOffsetTop === items[i].offsetTop) {
rowLength = 1;
} else {
break;
}
}
// event handler
function handleClick(event) {
for (let j = 0; j < itemLength; j ) {
let position;
if (items[j] === event.target && j < rowLength) {
// position for first row elements
position = j 1;
alert(`Position: ${position}`);
break;
} else if (items[j] === event.target && j >= rowLength) {
if ((j 1) % rowLength === 0) {
// position for last elements of each row after first row
position = rowLength;
alert(`Position: ${position}`);
break;
}
// position for rest of the row elements
alert(`Position: ${(j 1) % rowLength}`);
break;
}
}
}
// add onlick event
Array.from(items).forEach((item) => {
item.addEventListener("click", handleClick);
});
}
myFunc();
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
background-color: #ddd;
padding: 10px 0 0 10px;
margin-top: 5px;
overflow: auto;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}
<div id="grid" >
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>