网格是使用 CSS flexbox 实现的。 Example:
此示例中的行数为 4,因为我出于演示目的固定了容器宽度。但是,实际上,它可以根据容器的宽度而改变(例如,如果用户调整窗口大小)。尝试在 this example 中调整输出窗口的大小去感受一下。
总是有一个事件元素,用黑色边框标记。
使用 JavaScript,我允许用户使用左/右箭头导航到上一个/下一个元素。在我的实现中,我只是将事件项的索引减少/增加 1。
现在,我还想允许用户向上/向下导航。为此,我只需要通过 <amount of items in a row>
减少/增加事件元素的索引。 .但是,鉴于它取决于容器的宽度,我该如何计算这个数字?有没有更好的方法来实现向上/向下功能?
.grid {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 250px;
height: 200px;
background-color: #ddd;
padding: 10px 0 0 10px;
}
.item {
width: 50px;
height: 50px;
background-color: red;
margin: 0 10px 10px 0;
}
.active.item {
outline: 5px solid black;
}
<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>
最佳答案
这个问题比查找一行中有多少元素稍微复杂一些。
最终,我们想知道事件元素的上方、下方、左侧和右侧是否有元素。这需要考虑底行不完整的情况。例如,在下面的情况下,事件元素没有上方、下方或右侧的元素:
但是,为了确定事件元素的上方/下方/左侧/右侧是否有元素,我们需要知道一行中有多少元素。
查找每行的元素数
要获得每行的元素数,我们需要:
itemWidth
- outerWidth
单个元素包括 border
, padding
和 margin
gridWidth
- innerWidth
网格,不包括 border
, padding
和 margin
要使用纯 JavaScript 计算这两个值,我们可以使用:
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));
然后我们可以使用以下方法计算每行的元素数:
const numPerRow = Math.floor(gridWidth / itemWidth)
注:这仅适用于统一尺寸的物品,并且仅适用于
margin
在 px
中定义单位。一个非常非常简单的方法
处理所有这些宽度、填充、边距和边框真的很令人困惑。有一个非常非常简单的解决方案。
我们只需要找到
offsetTop
的网格元素的索引即可。属性大于第一个网格元素的 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);
最后的三元考虑了网格中只有一个元素和/或单行元素的情况。
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>
但是上面或下面有一个元素吗?
要知道事件元素上方或下方是否有元素,我们需要知道 3 个参数:
totalItemsInGrid
activeIndex
numPerRow
例如,在以下结构中:
<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>
我们有一个
totalItemsInGrid
的 5
, activeIndex
有一个从零开始的索引 2
(它是组中的第三个元素),让我们说 numPerRow
是 3。我们现在可以通过以下方式确定事件元素的上方、下方、左侧或右侧是否有元素:
isTopRow = activeIndex <= numPerRow - 1
isBottomRow = activeIndex >= totalItemsInGid - numPerRow
isLeftColumn = activeIndex % numPerRow === 0
isRightColumn = activeIndex % numPerRow === numPerRow - 1 || activeIndex === gridNum - 1
如
isTopRow
是 true
我们不能向上移动,如果isBottomRow
是 true
我们不能向下移动。如 isLeftColumn
是 true
我们不能向左移动,如果 isRightColumn
如果 true
我们不能向右移动。备注 :
isBottomRow
不仅检查事件元素是否在底行,还会检查其下方是否有元素。在我们上面的例子中,事件元素是 不是 在底行,但下面没有元素。一个工作示例
我已经把它变成了一个可以调整大小的完整示例 - 并制作了
#grid
元素可调整大小,因此可以在下面的代码段中进行测试。我创建了一个函数,
navigateGrid
接受三个参数:gridSelector
- 网格元素的 DOM 选择器 activeClass
- 事件元素的类名 direction
- up
之一, down
, left
, 或 right
这可以用作
'navigateGrid("#grid", "active", "up")
使用您问题中的 HTML 结构。该函数使用
offset
计算行数方法,然后检查是否 active
元素可以更改为上/下/左/右元素。换句话说,该函数检查事件元素是否可以向上/向下和向左/向右移动。这意味着:
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>
关于javascript - 如何计算连续 flexbox 元素的数量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49043684/