务实|内容滚动与导航标签互动关联方案
一、需求场景描述
1.先看演示效果
类似这种,当也页面左侧内容滚动的时候,需要关联激活左侧导航节点;当点击右侧导航节点时, 也会将左侧对应的内容滚动到可视区域顶部的场景,并不少见,比如知识类社区,掘金查看文章时,百度查看百科词条时,都有这种场景,而我的实际开发种也遇到此类需求。遂有此文。
2.需求分解:
1.滚动左侧内容,关联激活右侧导航节点
2.单击右侧导航节点,右侧相应的段落滚动到可视区顶部
二、关键技术点提前知
技术点1:Element.scrollIntoView()
1.scrollIntoView() 的作用:
scrollIntoView()方法会滚动元素的父容器,使被调用scrollIntoView()的元素对用户可见。
2.基本用法介绍:
var el = document.getElementById("p1");
// true 可以省略效果相同
el.scrollIntoView(true)
// alignToTop:Boolean型参数。
// 如果为 true,元素的顶端将和其所在滚动区的可视区域的顶端对齐。
// 如果为 false,元素的底端将和其所在滚动区的可视区域的底端对齐。
el.scrollIntoView(false);
// 可选,scrollIntoViewOptions:{behavior: "smooth", block: "end", inline: "nearest"}
// 可选,behavior :定义动画过渡效果,"auto"或 "smooth" 之一。默认为 "auto"。
// 可选,block:定义垂直方向的对齐,"start", "center", "end", 或 "nearest"之一。默认为 "start"。
// 可选,inline:定义水平方向的对齐,"start", "center", "end", 或 "nearest"之一。默认为 "nearest"。
el.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
el.scrollIntoView({block: "end"});
3.注意事项
普通的布局没问题,但是要注意,取决于其它元素的布局情况,此元素可能不会完全滚动到顶端或底端。比如整体上已经到顶部了,无法再滚动,那么该元素就不会移动到可视区的顶部。
技术点2:Element.getBoundingClientRect()
Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合,就是该元素的 CSS 边框大小。返回的结果是包含完整元素的最小矩形,并且拥有left, top, right, bottom, x, y, width, 和 height这几个以像素为单位的只读属性用于描述整个边框。除了width 和 height 以外的属性是相对于视图窗口的左上角来计算的。
下面是它的打印信息示例:
console.dir(document.getElementById("p1").getBoundingClientRect())
如果是标准盒子模型,元素的尺寸等于width/height + padding + border-width的总和。如果box-sizing: border-box,元素的的尺寸等于 width/height。
三、实现思路分析
1.数据初始化处理
// 从内容数据list中,获取段落标题作为导航标题。并为导航节点增加href,以段落的id值作为href的值
this.list.map((item, i) => {
this.activities.push({title: item.title, act: false, href: "#p" + i});
});
2.监听滚动条,以便滚动内容时,关联激活右侧导航标签
// 监听滚动条
window.addEventListener("scroll", function (e) {
// 防抖动处理
clearTimeout(that.timeout)
this.timeout = setTimeout(() => {
that.activeNavNode(e)
}, 100)
});
3.实现点击右侧导航节点,关联左侧内容滚动到可视区顶部
// dom中定位导航
navToPosition(item, index) {
// 激活相应的导航节点,变色
this.active = index;
// 根据导航节点的href信息即id信息,获取对应的元素节点,通过 scrollIntoView 滚动该元素到可视区顶部
document.querySelector(item.href).scrollIntoView(true);
},
4.实现滚动右侧内容,关联激活左侧导航节点
// 激活左侧对应的导航条
activeNavNode(e) {
const nodes = document.getElementsByTagName("section")
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
// 获取该元素此时相对于视口的顶部的距离,即是元素顶部距离视口屏幕上边的距离
let nodeY = node.getBoundingClientRect().y
// 当元素距离视口顶部的距离在 [0,200] 之间,设置激活该元素对应左侧的导航标题,这个数字可以按需定义
// 这里关联内容和导航标签,是巧妙利用了内容在元素集合中的索引序号和导航标签中的一致
// 即是 list 和 activities 和 nodes 中下标相等的元素,具有对应关联的关系
if (nodeY <= 200 && nodeY >= 0) {
this.active = Number(i)
return
}
}
},