重新计算/重排绝对定位元素网格的有效方法

时间:2017-03-28 06:21:25

标签: javascript dom

问题陈述

我有一个网格,元素角可能会或可能不会对齐,但所有边都接触,所以没有间隙。也没有重叠。例如,它可能有这样的东西:

+----+-------+
|    |   B   |
|    +---+---+
| A  | C |   |
|    |---| D |
|    | E |   |
+----+---+---+

网格是通过绝对定位的元素创建的。 (我意识到通过树创建这样的网格可能更容易,其中父节点是与相邻元素形成矩形的容器,但我认为这可能会限制我能够的方式调整元素大小 - 我稍后会解释。

我希望能够调整单个元素的大小并让相邻元素重新计算其尺寸,以便它们捕捉到新元素尺寸而不留下间隙。例如,我们假设我们正在调整元素C的大小:

  • 如果我将C的左边缘调整为A,我希望A水平缩小。由于A收缩,B和E都必须向A扩展以填补该空白。
  • 如果我调整C的下边缘,E应该缩小,不应该影响其他元素。
  • 如果我将C的右边缘调整到D,D应该缩小,E和C应该变成那个空隙。
  • 如果我将C的上边缘调整为B,则B应垂直缩小,D应随C扩展。

为什么树结构不起作用

现在,如前所述,我意识到将这些元素嵌套在容器元素(树状结构)中会更容易处理上述情况。我认为树形结构对我不起作用的原因(除了我已经有太多代码依赖于绝对位置的事实)是我不想要以下情况&# 39;调整大小取决于恰好位于下面的基础树结构:

+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+

使用树,此示例不起作用,因为中间图块调整大小会调整碰巧共享同一父/容器的元素,即使他们不需要调整大小。

当前的想法/工作

我试图弄清楚如何计算需要为我的绝对元素有效地调整哪些额外元素。我正在考虑以下几点:

  • 调整大小以使元素沿给定方向增长后,取相应的边缘并沿着此边缘以二进制搜索模式从一个角到另一个角执行{​​{1}},直到返回最小点的元素与对于每个采样点的最大点(如果它们不相同,则在中点采样新点并继续递归)。这组元素将包含元素因其大小调整而被入侵的所有元素(因此它们需要被相反的边缘缩小)
  • 在调整缩小元素的大小调整后,沿着原始边缘(调整大小之前)执行相同类型的二进制边缘遍历,但是在调整大小的相反方向上执行几个像素(这应该会触及需要增长的元素填补空白)
  • 对于主要元素,它将是上面的一个或另一个子弹(收缩或增长),但现在下一步是找到"副作用",如果相邻元素的边缘去超出原始元素的边缘,必须沿此扩展执行相同类型的分析。如果我们有类似砖的图案,这反过来可能会在同一边缘产生新的副作用。

在第一个子弹中解释的搜索将是这样的,然后我会检查后面的副作用:

document.elementsFromPoint()

我过度复杂吗?有更好的方法吗?这可以通过树木来实现,我只是没有看到它吗?

1 个答案:

答案 0 :(得分:1)

如果底层结构没有改变,那么你可以用树结构css flexbox来解决你的问题。

Flexbox是非常强大的布局工具,是现代浏览器引擎的原生工具。您可以使用css使用display: flex;以及其他简单的css来声明布局。

CSS技巧的flexbox教程可以比我更好地解释它,所以请参考this to understand what's going on。下面的代码更像是一个演示。

想法是改变元素的flexbox样式。要调整元素的大小,请使用javascript更改flex-basis我只有下面的按钮来显示概念证明,但最终,您希望使用鼠标事件来调整元素的大小。您可以将event.clientX除以容器宽度(container.clientWidth),以获得鼠标相对于容器的位置百分比,并将该值用于flexbasis。

在下面的演示中,我使用了一个变量,用于跟踪元素.a.a-complement的flexbasis。单击按钮时,每个元素的flexbasis更新。它们都以50%50%开始,每按一次按钮增长/缩小10%。可以扩展此示例以包含使用相同技术调整所有元素的大小。他们都会尊重彼此的大小,他们都没有差距等。

故事的道德:让布局引擎为您完成工作!除非你真的需要,否则不要使用绝对定位。

解决树结构问题:您可以在需要时将树移动div重组为其他div。如果这太复杂了,那么很遗憾浏览器可能没有对文档结构的原生支持。

但未来可能......

如果flexbox无法解决您的问题,那么实验性CSS GRID可能会越多,但请注意,CSS网格仅在最新的浏览器中实现,并且没有移动浏览器可能会给您的目标受众带来好处。

let aBasis = 0.5;
const elementA = document.querySelector('.a');
const aComplement = document.querySelector('.a-complement');
document.querySelector('#left').addEventListener('click', () => {
  aBasis -= 0.1;
  elementA.style.flexBasis = (aBasis * 100) + '%';
  aComplement.style.flexBasis = ((1 - aBasis) * 100) + '%';
  console.log((aBasis * 100) + '%', ((1 - aBasis) * 100) + '%');
});

document.querySelector('#right').addEventListener('click', () => {
  aBasis += 0.1;
  elementA.style.flexBasis = (aBasis * 100) + '%';
  aComplement.style.flexBasis = ((1 - aBasis) * 100) + '%';
  console.log((aBasis * 100) + '%', ((1 - aBasis) * 100) + '%');
});
.a {
  display: flex;
  background-color: red;
  flex: 1;
  justify-content: center;
  align-items: center;
}

.b {
  display: flex;
  background-color: blue;
  flex: 1;
  justify-content: center;
  align-items: center;
}

.c {
  display: flex;
  background-color: green;
  flex: 1;
  justify-content: center;
  align-items: center;
}

.d {
  display: flex;
  background-color: yellow;
  flex: 1;
  justify-content: center;
  align-items: center;
}

.e {
  display: flex;
  background-color: orange;
  flex: 1;
  justify-content: center;
  align-items: center;
}

.h-container {
  display: flex;
  align-items: stretch;
  flex: 1;
}

.v-container {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.container {
  display: flex;
  height: 200px;
  width: 200px;
  flex: 1
}

.example-container {
  display: flex;
  width: 100%;
}
<div class="example-container">
  <div class="container">
    <div class="h-container">
      <div class="a">
        <span>A</span>
      </div>
      <div class="a-complement v-container">
        <div class="b">
          <span>B</span>
        </div>
        <div class="h-container">
          <div class="v-container">
            <div class="c"><span>C</span></div>
            <div class="e"><span>E</span></div>
          </div>
          <div class="d"><span>D</span></div>
        </div>
      </div>
    </div>
  </div>
  <div>
    <button id="left">move a to the left</button>
    <button id="right">move a to the right</button>
  </div>
</div>