La question est un peu plus complexe que de trouver le nombre d'éléments dans une rangée.
En fin de compte, nous voulons savoir si il y a un élément ci-dessus, ci-dessous, à gauche et à droite de l'élément actif. Et cela doit prendre en compte les cas où la ligne du bas est incomplète. Par exemple, dans le cas ci-dessous, l'élément actif n'a pas de point ci-dessus, en dessous ou à droite:
Mais, afin de déterminer s'il y a un point au-dessus/ci-dessous/à gauche/à droite de l'élément actif, nous avons besoin de savoir combien d'éléments dans une rangée.
Trouver le nombre d'éléments par ligne
Pour obtenir le nombre d'éléments par ligne nous avons besoin de:
-
itemWidth
- l' outerWidth
d'un seul élément, y compris border
, padding
et margin
-
gridWidth
- l' innerWidth
de la grille, à l'exclusion border
, padding
et margin
Pour calculer ces deux valeurs avec du JavaScript que l'on peut utiliser:
const itemStyle = singleItem.currentStyle || window.getComputedStyle(active);
const itemWidth = singleItem.offsetWidth + parseFloat(itemStyle.marginLeft) + parseFloat(itemStyle.marginRight);
const gridStyle = grid.currentStyle || window.getComputedStyle(grid);
const gridWidth = grid.clientWidth - (parseFloat(gridStyle.paddingLeft) + parseFloat(gridStyle.paddingRight));
Ensuite, nous pouvons calculer le nombre d'éléments par ligne à l'aide de:
const numPerRow = Math.floor(gridWidth / itemWidth)
Remarque: cela ne fonctionne que pour les uniformes de taille moyenne, et seulement si l' margin
est définie en px
des parts.
Beaucoup, Beaucoup, Beaucoup Plus Simple
Sur l'ensemble de ces largeurs, et les rembourrages, les marges et les frontières, c'est vraiment déroutant. Il y a beaucoup, beaucoup, beaucoup plus simple solution.
Nous avons seulement besoin de trouver l'index de l'élément de grille qui est - offsetTop
de la propriété est plus grande que la première grille de l'élément offsetTop
.
const grid = Array.from(document.querySelector("#grid").children);
const baseOffset = grid[0].offsetTop;
const breakIndex = grid.findIndex(item => item.offsetTop > baseOffset);
const numPerRow = (breakIndex === -1 ? grid.length : breakIndex);
Le ternaire à la fin des comptes pour les cas où il n'y a qu'un seul élément de la grille, et/ou une seule rangée d'éléments.
const getNumPerRow = (selector) => {
const grid = Array.from(document.querySelector(selector).children);
const baseOffset = grid[0].offsetTop;
const breakIndex = grid.findIndex(item => item.offsetTop > baseOffset);
return (breakIndex === -1 ? grid.length : breakIndex);
}
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 400px;
background-color: #ddd;
padding: 10px 0 0 10px;
margin-top: 5px;
resize: horizontal;
overflow: auto;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}
<button onclick="alert(getNumPerRow('#grid'))">Get Num Per Row</button>
<div id="grid" class="grid">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item active"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Mais est-il un élément au-dessus ou en-dessous?
Pour savoir si il y a un point au-dessus ou au-dessous de l'élément actif, nous avons besoin de savoir 3 paramètres:
totalItemsInGrid
activeIndex
numPerRow
Par exemple, dans la structure suivante:
<div id="grid" class="grid">
<div class="item"></div>
<div class="item"></div>
<div class="item active"></div>
<div class="item"></div>
<div class="item"></div>
</div>
nous avons un totalItemsInGrid
de 5
, l' activeIndex
a un index de base zéro de l' 2
(c'est le 3ème élément dans le groupe), et disons que l' numPerRow
est de 3.
Nous pouvons maintenant déterminer si un élément est au dessus, en dessous, à gauche ou à droite de l'élément actif avec:
isTopRow = activeIndex <= numPerRow - 1
isBottomRow = activeIndex >= totalItemsInGid - numPerRow
isLeftColumn = activeIndex % numPerRow === 0
isRightColumn = activeIndex % numPerRow === numPerRow - 1 || activeIndex === gridNum - 1
Si isTopRow
est true
on ne peut pas se déplacer jusqu', et si isBottomRow
est true
on ne peut pas se déplacer vers le bas. Si isLeftColumn
est true
on ne peut pas se déplacer à gauche, et si isRightColumn
si true
nous ne pouvons pas nous déplacer vers la droite.
Remarque: isBottomRow
n'est pas seulement de vérifier si l'élément actif est sur la ligne du bas, mais vérifie également si il y a un élément en dessous. Dans notre exemple ci-dessus, l'élément actif est pas sur la ligne du bas, mais n'a pas un point en-dessous d'elle.
Un Exemple De Travail
J'ai travaillé dans un exemple complet qui fonctionne avec le redimensionnement et l' #grid
élément redimensionnable, de sorte qu'il peut être testé dans l'extrait de code ci-dessous.
J'ai créé une fonction, navigateGrid
qui accepte trois paramètres:
-
gridSelector
- un DOM sélecteur pour l'élément de grille
-
activeClass
- le nom de la classe de l'élément actif
-
direction
- l'un d' up
, down
, left
ou right
Il peut être utilisé comme 'navigateGrid("#grid", "active", "up")
avec la structure HTML de votre question.
La fonction calcule le nombre de lignes à l'aide de l' offset
méthode, puis ne la vérifie si l' active
élément peut être modifié pour le haut/bas/gauche/droite de l'élément.
En d'autres termes, la fonction vérifie si l'élément actif peut être déplacé vers le haut/bas et gauche/droite. Cela signifie:
- ne peut pas aller à gauche de la colonne la plus à gauche
- peut pas aller à droite de la colonne la plus à droite
- ne peut pas monter à partir de la rangée du haut
- ne peut pas aller vers le bas à partir de la rangée du bas, ou si la cellule ci-dessous est vide
const navigateGrid = (gridSelector, activeClass, direction) => {
const grid = document.querySelector(gridSelector);
const active = grid.querySelector(`.${activeClass}`);
const activeIndex = Array.from(grid.children).indexOf(active);
const gridChildren = Array.from(grid.children);
const gridNum = gridChildren.length;
const baseOffset = gridChildren[0].offsetTop;
const breakIndex = gridChildren.findIndex(item => item.offsetTop > baseOffset);
const numPerRow = (breakIndex === -1 ? gridNum : breakIndex);
const updateActiveItem = (active, next, activeClass) => {
active.classList.remove(activeClass);
next.classList.add(activeClass);
}
const isTopRow = activeIndex <= numPerRow - 1;
const isBottomRow = activeIndex >= gridNum - numPerRow;
const isLeftColumn = activeIndex % numPerRow === 0;
const isRightColumn = activeIndex % numPerRow === numPerRow - 1 || activeIndex === gridNum - 1;
switch (direction) {
case "up":
if (!isTopRow)
updateActiveItem(active, gridChildren[activeIndex - numPerRow], activeClass);
break;
case "down":
if (!isBottomRow)
updateActiveItem(active, gridChildren[activeIndex + numPerRow], activeClass);
break;
case "left":
if (!isLeftColumn)
updateActiveItem(active, gridChildren[activeIndex - 1], activeClass);
break;
case "right":
if (!isRightColumn)
updateActiveItem(active, gridChildren[activeIndex + 1], activeClass);
break;
}
}
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 400px;
background-color: #ddd;
padding: 10px 0 0 10px;
margin-top: 5px;
resize: horizontal;
overflow: auto;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}
<button onClick='navigateGrid("#grid", "active", "up")'>Up</button>
<button onClick='navigateGrid("#grid", "active", "down")'>Down</button>
<button onClick='navigateGrid("#grid", "active", "left")'>Left</button>
<button onClick='navigateGrid("#grid", "active", "right")'>Right</button>
<div id="grid" class="grid">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item active"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>