返回

Split Panes 实现同步滚动与固定表头 (Vue)

vue.js

分割面板(Split Panes)中实现同步滚动与固定表头

在前端开发中,使用分割面板组件(如 splitpanes)来实现分屏比较或类似需求的情况非常常见。将一个页面或组件分成两个或多个独立的可调整大小的窗格,对于开发者们调试代码或做数据比对尤为便捷。而分割面板内实现同步滚动,以及每个窗格设置固定表头则更加高效和有用。

核心问题与难点

问题的主要表现在于两个方面:

  • 同步滚动 : 当一个窗格中的内容发生滚动时,另一个窗格也需要相应地进行滚动,以保持内容同步。
  • 固定表头 : 每个窗格都应有一个固定的头部,以便在滚动内容时始终保持可见。

通常情况下,为每个窗格设置 overflow-y: auto 可以轻松实现独立滚动。然而,同步滚动和固定表头功能的实现更具挑战性。尤其是在一起使用两者时,情况更加复杂。

解决方案:同步滚动

同步滚动的关键是监听一个窗格的滚动事件,并将其同步到另一个窗格。下面提供了使用原生 JavaScript 和一些常用库实现同步滚动的解决方案。

方案一:利用 ref 引用和事件监听

此方法在组件加载完成后,对窗格进行操作,并添加事件监听器来触发函数同步窗格滚动。通过监听其中一个窗格的滚动事件,并将滚动值应用到另一个窗格上,来实现同步效果。

步骤:

  1. 给每个 Pane 组件添加 ref 属性,以便在组件内访问它们。
  2. mounted 生命周期钩子中,获取窗格的 DOM 元素,为其中一个窗格添加 scroll 事件监听器。
  3. 在事件处理函数中,获取触发滚动事件窗格的 scrollTop 值,并将其赋值给另一个窗格的 scrollTop

代码示例:

<template>
  <v-container fluid>
    <Splitpanes class="default-theme" style="height: 400px">
      <Pane :key="0" ref="pane0">
        <div class="pa-4">
          <div>Header</div>
          <div ref="content0">
            <!-- Very long text... -->
          </div>
        </div>
      </Pane>
      <Pane :key="1" ref="pane1">
        <div class="pa-4">
          <div>Header</div>
          <div ref="content1">
            <!-- Very long text... -->
          </div>
        </div>
      </Pane>
    </Splitpanes>
  </v-container>
</template>

<script>
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'

export default {
  components: {
    Splitpanes,
    Pane,
  },
  mounted() {
    this.panes = [this.$refs.content0, this.$refs.content1];
    this.$refs.content0.addEventListener('scroll', this.syncScroll);
  },
  beforeDestroy() {
    this.$refs.content0.removeEventListener('scroll', this.syncScroll);
  },
  methods: {
    syncScroll(event) {
      this.panes[1].scrollTop = event.target.scrollTop;
    }
  },
};
</script>

<style scoped>
.splitpanes__pane {
  overflow-y: auto;
}
</style>

通过ref引用绑定了panes中的滚动区域DOM,从而通过监听器设置对应的滚动操作。beforeDestroy可以用来在组件被销毁前,解除该事件监听器。当然也可以将内容区域的dom操作移动至更精确的位置。
此方法确保了内容同步性,避免多余计算,高效简洁。

解决方案:固定表头

sticky定位,这种看似简单的样式属性在不同环境下表现行为各异。这为应用带来了不确定性。
特别是在分割窗格的环境下使用,更应注意。

常见问题:position: sticky 失效

将表头元素的 position 属性设置为 sticky,是实现固定表头的常用方法。这种方式通过一个css样式就可以实现,无需 JavaScript 控制。但当使用了不当的CSS 样式属性、HTML 元素层级关系或者其父级元素设置了 overflow 属性都可能使其失效。

问题分析:

  • 父级元素属性问题:在复杂布局中,父元素 overflow 不为 visible,可能影响 sticky 的生效。
  • sticky的定位规则是基于用户当前的 viewport 滚动来定义的。

方案一:纯CSS 解决方案

操作步骤:

  1. 确保表头元素应用了 position: sticky
  2. 为表头元素设置 top 属性,通常设置为 0
  3. 检查表头父元素至其最顶层的HTML body元素。需要注意将 .splitpanes__pane设置正确的overflow-y,如果其本身需要使用到overflow 属性,应当避免直接添加该属性。将其overflow添加至内部元素。

代码示例:

<template>
  <v-container fluid>
    <Splitpanes class="default-theme" style="height: 400px">
      <Pane :key="0" ref="pane0">
          <div class="pane-content">
            <div class="sticky">Header</div>
            <div class="pa-4" ref="content0">
              <!-- Very long text... -->
            </div>
          </div>
      </Pane>
      <Pane :key="1" ref="pane1">
          <div class="pane-content">
            <div class="sticky">Header</div>
            <div class="pa-4" ref="content1">
              <!-- Very long text... -->
            </div>
          </div>
      </Pane>
    </Splitpanes>
  </v-container>
</template>

<script>
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'

export default {
  components: {
    Splitpanes,
    Pane,
  },
  mounted() {
    this.panes = [this.$refs.content0, this.$refs.content1];
    this.$refs.content0.addEventListener('scroll', this.syncScroll);
  },
  beforeDestroy() {
    this.$refs.content0.removeEventListener('scroll', this.syncScroll);
  },
  methods: {
    syncScroll(event) {
      this.panes[1].scrollTop = event.target.scrollTop;
    },
  },
};
</script>

<style scoped>
.pane-content {
    overflow-y: auto;
}

.sticky {
  position: sticky;
  top: 0;
  background-color: white; /* Ensure header is visible against content */
  z-index: 10; /* Adjust as needed to layer properly */
}
</style>

为每个pane添加.pane-content 元素,并设置 overflow-y: auto;避免直接为.splitpanes__pane添加样式。避免出现设置的overflow影响sticky属性的情况。添加sticky样式以定义position: sticky的元素在什么位置定位。这样就可以让 sticky 在不同的布局条件下都能表现正常。在处理页面内容时,一定要保持良好的代码组织和简洁性。通过合理的样式规划以及明晰的结构布局实现既定需求。这可以增强代码的可读性和可维护性,使后续开发过程更为顺利。