1.最初のコード:
<template>
<div>
<h1>页面内容滚动与右侧导航标签互动关联 效果演示 demo</h1>
<div class="content" id="content">
<div class="left">
<section v-for="(item, i) in list" class="text-node" :key="i" :id="'p' + i">
<div class="title">
<div class="title-pre"></div>
<div v-html="item.title"></div>
</div>
<div class="text">
<div v-html="item.text"></div>
</div>
</section>
</div>
<div class="right">
<div class="entry-message">
<div class="title">内容导航</div>
<div class="content-anchor">
<div>
<el-timeline>
<el-timeline-item
v-for="(item, index) in activities"
:key="index"
:color="active === index ? '#2b6afb' : ''"
>
<div
@click="navToPosition(item, index)"
:style="{ color: active === index ? '#2b6afb' : '' }"
>
<span class="cn-pointer" v-html="item.title"></span>
</div>
</el-timeline-item>
</el-timeline>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// 示例样本内容,提前声明赋值,方便后面复用
const text =
"1、能源资源\n" +
"能源资源包括煤、石油、天然气、水能等,也包括太阳能、风能、生物质能、地热能、海洋能、核能等新能源。纵观社会发展史,人类经历了柴草能源时期、" +
"煤炭能源时期和石油、天然气能源时期,正向新能源时期过渡,并且无数学者仍在不懈地为社会进步寻找开发更新更安全的能源。但是," +
"人们能利用的能源仍以煤炭、石油、天然气为主,在世界一次能源消费结构中,这三者的总和约占93%。\n" +
"在一定历史时期和科学技术水平下,已被人们广泛应用的能源称为常规能源。那些虽古老但需采用新的先进的科学技术才能加以广泛应用的能源称为新能源。" +
"凡在自然界中可以不断再生并有规律地得到补充的能源,称为可再生能源。经过亿万年形成的,在短期内无法恢复的能源称为非可再生能源。"
export default {
name: "ScrollIntoView",
data() {
return {
list: [
{
title: "能源开发与利用1", text: text},
{
title: "能源开发与利用2", text: text},
{
title: "能源开发与利用3", text: text},
{
title: "能源开发与利用4", text: text},
{
title: "能源开发与利用5", text: text},
{
title: "能源开发与利用6", text: text},
],
active: 0,
timeout: null,
activities: [],
};
},
mounted() {
console.dir(document.getElementById("p1").getBoundingClientRect())
this.init()
},
methods: {
init() {
const that = this
// 从内容数据list中,获取段落标题作为导航标题。并为导航节点增加href,以段落的id值作为href的值
this.list.map((item, i) => {
this.activities.push({
title: item.title, act: false, href: "#p" + i});
});
// 监听滚动条
window.addEventListener("scroll", function (e) {
// 防抖动处理
clearTimeout(that.timeout)
this.timeout = setTimeout(() => {
that.activeNavNode(e)
}, 100)
});
},
// dom中定位导航
navToPosition(item, index) {
// 激活相应的导航节点,变色
this.active = index;
// 根据导航节点的href信息即id信息,获取对应的元素节点,通过 scrollIntoView 滚动该元素到可视区顶部
document.querySelector(item.href).scrollIntoView(true);
},
// 激活左侧对应的导航条
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
}
}
},
},
};
</script>
<style lang="scss" scoped>
.cn-pointer {
cursor: pointer;
}
.content {
display: flex;
padding: 20px 0 100px 0;
width: 1100px;
min-width: 1100px;
max-width: 1100px;
align-content: center;
margin: 0 auto;
.left {
flex-grow: 1;
margin: 0 20px 500px 0;
padding: 0 40px 40px;
background: #ffffff;
box-shadow: 0 2px 20px -6px rgba(30, 135, 240, 0.1);
border-radius: 4px;
}
section {
padding: 30px 0;
.title {
height: 25px;
display: flex;
align-items: center;
margin-bottom: 20px;
font-size: 18px;
color: #262626;
font-weight: bold;
margin-top: 20px;
.title-pre {
width: 9px;
height: 18px;
margin-right: 10px;
background: #1e87f0;
}
}
.text {
font-size: 14px;
color: #262626;
line-height: 44px;
font-weight: 400;
text-align: left;
span {
display: block;
margin-bottom: 20px;
}
}
}
.right {
min-width: 300px
}
.entry-message {
box-sizing: content-box;
padding: 26px 30px;
background: #ffffff;
box-shadow: 0 2px 20px -6px rgba(30, 135, 240, 0.1);
border-radius: 4px;
position: fixed;
top: 105px;
.title {
margin-bottom: 24px;
font-size: 18px;
color: #262626;
font-weight: 600;
}
.content-anchor {
display: flex;
flex-direction: row;
align-items: center;
}
}
}
</style>
効果:
予約が必要な知識ポイント1:
テクニカルポイント1:Element.scrollIntoView()。
ElementインターフェースのscrollIntoView()メソッドは、要素の親コンテナーをスクロールし、scrollIntoView()が呼び出された要素をユーザーに表示します。
> 语法
>
element.scrollIntoView(); // 等同于element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // Boolean型参数
element.scrollIntoView(scrollIntoViewOptions); // Object型参数
テクニカルポイント2:Element.getBoundingClientRect()
Element.getBoundingClientRect()メソッドは、要素のサイズとビューポートに対する相対的な位置を返します。戻り値はDOMRectオブジェクトであり、要素のCSS境界サイズである要素のgetClientRects()メソッドによって返される長方形のコレクションです。返される結果は、完全な要素を含む最小の長方形であり、境界全体を表す左、上、右、下、x、y、幅、および高さのピクセル単位の読み取り専用プロパティがあります。幅と高さ以外のプロパティは、ビューウィンドウの左上隅を基準にして計算されます。
getBoundingClientRectmdnAddress
実装の難しさは、コンテンツが変更されていることです。右側のディレクトリをアクティブ化するにはどうすればよいですか?
- この時点でのビューポートの上部に対する要素の距離、つまり、要素の上部からビューポート画面の上部までの距離を取得します。
- 要素コレクション内のコンテンツのインデックス番号とナビゲーションラベルの一貫性を巧みに利用します。