实现一个 Vue 吸顶锚点组件方法(2)

由于我的需求有一些是需要做锚点定位功能,但是直接用锚点定位会改变路由所以改为了滚动定位(后面会细说)。但是由于吸顶组件在 `fixed` 之后会脱离文档流,导致定位的元素会有一部分(吸顶组件高度 )被卡在吸顶组件下方。就像下面这张图的效果,右边的锚点定位2区域的标题被隐藏了。

实现一个 Vue 吸顶锚点组件方法

这些问题也很好解决,使用一个和吸顶组件相同大小的占位元素,当吸顶组件脱离文档流之后,占位元素插入吸顶组件原来的 DOM 位置中,然后顺便带上一些小优化。由于占位元素需要和组件高度一致,所以必须要保证 `slot` 插槽中的 DOM 元素已经被加载完成,另外放在 slot 元素中可能发生变更,所以我在吸顶状态变更之前获取其高度。

<template> <div> <div :class="fixedClass" :style="{ top: top + 'px', zIndex }"> <slot></slot> </div> <div v-if="showPlaceholder" :style="{ height: offsetHeight + 'px' }"></div> </div> </template> <script> import { getFirstScrollElement } from 'util.js' export default { props: { top: { type: Number, default: 0 }, zIndex: { type: Number, default: 0 }, parent: { type: String, default: '' } }, data() { return { isMounted: false, fixedClass: '', offsetHeight: 0, scrollElement: null, showPlaceholder: false } }, mounted() { this.isMounted = true this.initScrollElement() }, watch: { parent: { immediate: true, handler: 'getScrollElement' }, fixedClass(v) { if (v && !this.offsetHeight) { this.offsetHeight = this.$el.offsetHeight } this.showPlaceholder = !!v } }, destroyed() { this.removeScrollEvent() }, methods: { handleScroll(e) { const scrollOffsetTop = this.$el.offsetTop - this.top if (this.scrollElement.scrollTop >= scrollOffsetTop) { this.fixedClass = 'top-fixed' } else { this.fixedClass = '' } }, initScrollElement() { if (!this.isMounted) return const parent = this.parent let element = null if (parent) { element = document.querySelector(parent) if (element === this.scrollElement) return } else if (this.$el) { element = getFirstScrollElement(this.$el) } if (element) { this.removeScrollEvent() this.scrollElement = element this.scrollElement.addEventListener('scroll', this.handleScroll) } }, removeScrollEvent() { if (this.scrollElement) { this.scrollElement.removeEventListener('scroll', this.handleScroll) } } } } </script> <style lang="scss"> .cpt-sticky { .top-fixed { position: fixed; width: 100%; background: #fff; } } </style>

锚点定位

网页中经常会有用到锚点定位的场景,例如百度知道的目录,我目前知道有三种方式可以实现这种功能。

使用 a 标签定位

使用 js 定位

使用 a 标签定位

先说说 a 标签定位,这是一种最常用的定位方式。它有两种实现方式,一种是通过 herf 属性链接的指定元素的 id。另一种是添加一个 a 标签,再将 href 属性链接到这个 a 标签的 name 属性。

<a href="#view1">按钮1</a> <a href="#view2">按钮1</a> ... <div>视图1</div> <div><a>视图2</a></div>

这种定位方式很简单,它支持任意标签定位。不过它也存在一些问题,例如如果滚动区内有固定或绝对定位,会出现遮罩问题,还有瞬间滚动到顶部,交互不是很好,当然这些都可以通过 css 解决。但最主要问题是,a 标签定位会改变路由的 hash,如果有相应的路由的话会进行路由跳转。

通过 js 模拟锚点定位

通过 js 去操作元素的 `scrollTop` 等属性,使其滚动到父级滚动元素指定的位置,就能实现定位效果。这里简单提一下 `scrollIntoView()` 这个方法,根据MDN 的描述,`Element.scrollIntoView()` 方法让当前的元素滚动到浏览器窗口的可视区域内。`scrollIntoView()` 还支持动画的选项,通过 `behavior` 设置,不过遗憾的是它遇到固定定位也会出现遮盖的问题,所以最终选择手动去撸码,不过 `scrollIntoView()` 倒是很适合做回到顶部这种功能。

首先我们需要让按钮和滚动区内容元素建立对应关系,在按钮的值中放入对应的内容区元素的 css 选择器,根据点击按钮的值找到对应的元素。所以计算规则是这个元素距离滚动区的高度加上这个元素上边距的高度(我在内容区加了外边距,我希望显示它),减去滚动区距离可视区的高度(我的页面没有定位,所以 offsetTop 对应可视区),再减去按钮组件的高度,就可以得出需要滚动的位置。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/a494f6f30d5249d12d32871655426aac.html