Content and catalog (tag) association interaction scheme

1. First code:

<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>

Effect:
insert image description here

Knowledge points that need to be reserved 1:

Technical point 1: Element.scrollIntoView().

The scrollIntoView() method of the Element interface scrolls the element's parent container, making the element on which scrollIntoView() is called visible to the user.

> 语法
> 
element.scrollIntoView(); // 等同于element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // Boolean型参数
element.scrollIntoView(scrollIntoViewOptions); // Object型参数

Technical point 2: Element.getBoundingClientRect()
The Element.getBoundingClientRect() method returns the size of the element and its position relative to the viewport. The return value is a DOMRect object, which is a collection of rectangles returned by the element's getClientRects() method, which is the element's CSS border size. The returned result is the smallest rectangle that contains the complete element, and has read-only properties in pixels, left, top, right, bottom, x, y, width, and height, that describe the entire border. Properties other than width and height are calculated relative to the upper left corner of the view window.
getBoundingClientRectmdnAddress

The difficulty of implementation is that the content has changed. How to activate the directory on the right?

  1. Get the distance of the element relative to the top of the viewport at this time, that is, the distance from the top of the element to the top of the viewport screen.
  2. Cleverly makes use of the consistency of the index number of the content in the element collection and the navigation label .

Guess you like

Origin blog.csdn.net/qq_42931285/article/details/124367633