javascript - 如何计算连续 flexbox 元素的数量?

标签 javascript html css flexbox

网格是使用 CSS flexbox 实现的。 Example:

enter image description here

此示例中的行数为 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>

最佳答案

这个问题比查找一行中有多少元素稍微复杂一些。

最终,我们想知道事件元素的上方、下方、左侧和右侧是否有元素。这需要考虑底行不完整的情况。例如,在下面的情况下,事件元素没有上方、下方或右侧的元素:

enter image description here

但是,为了确定事件元素的上方/下方/左侧/右侧是否有元素,我们需要知道一行中有多少元素。

查找每行的元素数

要获得每行的元素数,我们需要:

  • itemWidth - outerWidth单个元素包括 border , paddingmargin
  • gridWidth - innerWidth网格,不包括 border , paddingmargin

  • 要使用纯 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)
    

    注:这仅适用于统一尺寸的物品,并且仅适用于 marginpx 中定义单位。

    一个非常非常简单的方法

    处理所有这些宽度、填充、边距和边框真的很令人困惑。有一个非常非常简单的解决方案。

    我们只需要找到 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>
    

    我们有一个 totalItemsInGrid5 , activeIndex有一个从零开始的索引 2 (它是组中的第三个元素),让我们说 numPerRow是 3。

    我们现在可以通过以下方式确定事件元素的上方、下方、左侧或右侧是否有元素:
  • isTopRow = activeIndex <= numPerRow - 1
  • isBottomRow = activeIndex >= totalItemsInGid - numPerRow
  • isLeftColumn = activeIndex % numPerRow === 0
  • isRightColumn = activeIndex % numPerRow === numPerRow - 1 || activeIndex === gridNum - 1

  • isTopRowtrue我们不能向上移动,如果isBottomRowtrue我们不能向下移动。如 isLeftColumntrue我们不能向左移动,如果 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/

    相关文章:

    动画时 HTML 元素移动不畅

    javascript - 如何在 javascript 中突出显示找到的文本

    javascript - 官方 dygraphs.com Synchronize.html 演示已被破坏 : my version too

    jquery - ipad - 位置 :fixed bug on zoom in

    html - 放大时剪切背景图像

    html - CSS & HTML 方形背景

    PHP-HTML-CSS - 国际化和样式

    javascript - 如何设置在页面加载时更改的 div 的整页背景图像

    javascript - 如何选择注入(inject)的 HTML 的 li 并使其隐藏

    javascript - 悬停时向上滑动,悬停时向下滑动(没有独特的类别)