Vue: Realize anchor point two-way scrolling/article chapter linkage scrolling effect

Description of Requirement

It is necessary to achieve an effect similar to the document outline in doc, click on the name of the corresponding chapter to locate the corresponding text; and when the text scrolls, highlight the corresponding chapter name
Chapter linkage renderings

Implementation ideas

In fact, what the author thought of at the beginning was to use the jump in the a tab (that is, "anchor point"), which is similar to the official Element-UI document: when the mouse hovers over a chapter name in the page, the radio button group
will display The anchor point with the symbol "¶", click on the anchor point will stitch the corresponding hash value after the web page link to realize the in-page jump,
Hungry? Official UI Documentation - Anchor Points for In-Page Jumps
but when it is difficult to scroll the page content only by the anchor point, locate the corresponding chapter name. . It still depends on Du Niang
. After some searching and trying, I found that all the articles marked with "vue anchor point two-way scrolling" actually use the implementation idea of ​​monitoring scrolling/scrolling pages and "anchor point" is actually not related. . , in short, the author finally turned to this anchor point two-way positioning blog, although the sample code of the original text is a bit rough, butThe essence of this article is actually the blog post that has been personally polishedOh ☆♦☆!

sample code

Please create a new test.vue in your local project that introduced Element-UI to run the following sample code~

<!--
 * @Author: smm
 * @Date: 2022-09-19 15:33:32
 * @LastEditors: smm
 * @LastEditTime: 2022-09-19 17:27:08
 * @Description: 章节联动滚动示例
-->
<template>
  <div class="sectionTest">
    <div>
      <el-radio-group
        v-model="sectionIdx"
        @change="change"
      >
        <el-radio
          v-for="(item, index) in sectionList"
          :key="item"
          :label="index"
        >
          {
   
   { item }}
        </el-radio>
      </el-radio-group>
    </div>
    <div
      class="contentBlock"
      @scroll="scrollEvent"
    >
      <div
        v-for="item in sectionList"
        :key="item"
        class="content"
      >
        {
   
   { `正文:${item}` }}
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: ``,
  data () {
    return {
      sectionList: [`第一章`, `第二章`, `第三章`, `第四章`, `第五章`], // 章节标题列表
      sectionIdx: 0 // 当前的章节下标
    }
  },
  methods: {
    change () {
      let contents = document.querySelectorAll('.content')
      contents[this.sectionIdx].scrollIntoView({ block: 'start', behavior: 'smooth' })
    },
    scrollEvent (e) {
      const { sectionList } = this
      let contents = document.querySelectorAll('.content')
      let nowTop = e.target.scrollTop // 滚动条目前的滚动距离
      let firstTop = contents[0].offsetTop // 第一个章节的顶部位置
      let idx = null
      try { // forEach循环只能通过抛出异常的方式终止
        sectionList.forEach((section, index) => {
          let contentTop = contents[index].offsetTop // 第index个章节的顶部位置
          let scrollDistance = contentTop - firstTop // 从第一个章节到第index个章节的滚动距离
          if (nowTop < scrollDistance) {
            // 滚动条已滚动的距离少于第index个章节需要的滚动距离,说明当前正文处于第(index-1)的章节
            idx = Math.max(0, index - 1)
            throw new Error(`find section:${this.sectionIdx}`)
          }
        })
      } catch (e) { }
      // 滚动到最后一章(该章节高度超过正文容器)时,滚动条的滚动距离超过所有章节需要的滚动距离
      this.sectionIdx = idx === null ? sectionList.length - 1 : idx
      if (e.srcElement.scrollTop + e.srcElement.offsetHeight === e.srcElement.scrollHeight) {
        // 滚动条触底,处于最后一章(消除该章节高度低于正文容器时始终定位在倒数第二章的情况)
        this.sectionIdx = sectionList.length - 1
      }
    }
  }
}
</script>

<style lang="scss">
.sectionTest {
  height: 500px;
  background: #fff;
  padding: 0 20px;
  .contentBlock{
    height: 400px;
    overflow-y: auto;
    &::-webkit-scrollbar {
      width: 10px;
      height: 10px;
    }
    &::-webkit-scrollbar-thumb {
      background:#ccc;
      border: 1px solid green;
      border-radius: 5px;
    }
    &::-webkit-scrollbar-track {
      background: #666;
      border-radius: 5px;
    }
  }
  .content {
    border: 1px solid red;
    margin: 10px 0;
    color: orangered;
    &:nth-child(even) {
      height: 500px;
      background: #aaa;
    }
    &:nth-child(odd) {
      height: 300px;
      background: #eee;
    }
  }
}
</style>

Reference URL

[1] Anchor point two-way positioning
[2] Element-UI radio button group
[3] forEach in Js jumps out of this loop and terminates the loop

Guess you like

Origin blog.csdn.net/qq_36604536/article/details/126936016