Problem description

Left-right linkage is common on mobile terminals. For example, in Meituan takeout, merchants choose takeaway goods. The common solution is to use the Better scroll library to achieve. Occasionally, though, projects on the Web side will have this effect. This article uses native JS in the VUE framework to achieve the corresponding effect. Let’s take a look at the final result:

Code attached

Comments in the code are written with thought steps. Please follow the comment thread steps.

<template>
  <div id="app">
    <div class="top">
      <h2>Vue uses native JS to achieve the scrolling effect on the Web side</h2>
    </div>
    <div class="bottom">
      <! -- Left menu bar -->
      <div class="bottomLeft">
        <div
          class="leftItem"
          v-for="(item0, index0) in leftArr"
          :key="index0"
          :class="{ highLight: whichIndex == index0 }"
          @click="letItemHighLight(index0)"
        >
          {{ item0 }}
        </div>
      </div>
      <! -- The content on the right of the left menu bar -->
      <div class="bottomRight" ref="wrapper">
        <div
          class="bottomRightContent"
          v-for="(item, index) in rightArr"
          :key="index"
          ref="item"
        >
          <div class="bottomRightContentHead">{{ item.titleOne }}</div>
          <div class="bottomRightContentBody">
            <el-col :span="8" v-for="(item2, index2) in item.titleTwo" :key="index2">
              <span class="circle"></span>
              <span class="word">{{ item2 }}</span>
            </el-col>
            <! Clear the float -->
            <div style="clear: both"></div>
          </div>
          <div class="bottomRightContentFooter"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      whichIndex: 0.// Dynamic display of the left menu bar highlight
      leftArr: [].// Data for the left menu bar
      rightArr: [].// The corresponding data is displayed in details on the right
      rightHeightArr: [].// The height array of each item on the right
      rightHeightSumArr: [].// Add the height of each item on the right
      r: 0.// The scrolling distance
    };
  },
  mounted() {
    // The first step is to get the data on the left and right sides of the request, which is used to render the page
    this.getLeftArrData();
    this.getRightArrData();
  },
  methods: {
    getLeftArrData() {
      let apiLeftArr = [
        Journey to the West.Romance of The Three Kingdoms.A Dream of Red Mansions."Water Margin"."Dragon"."Visionary".Inuyasha.One Piece."Punch Superman.".Wolverine."Iron Man"."Destroy the bully"."Thor"."Playing with the Blue Moon"."Dream Journey west".Honor of Kings,];this.leftArr = apiLeftArr;
    },
    getRightArrData() {
      let apiRightArr = [
        {
          titleOne: Journey to the West.titleTwo: ["111"."222"."333"."444"],}, {titleOne: Romance of The Three Kingdoms.titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: A Dream of Red Mansions.titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Water Margin".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Dragon".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Visionary".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: Inuyasha.titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: One Piece.titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Punch Superman.".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: Wolverine.titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Iron Man".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Destroy the bully".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Thor".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Playing with the Blue Moon".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: "Dream Journey west".titleTwo: ["111"."222"."333"."444"."111"."222"."333"."444"],}, {titleOne: Honor of Kings.titleTwo: [
            "111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444"."111"."222"."333"."444",]}];this.rightArr = apiRightArr;
      // The height array can be calculated only when there is data on the left and right sides. Watch your priorities
      // Use this.$nextTick() to defer the callback, which evaluates the two height arrays, until after the next DOM update loop
      this.$nextTick(() = > {
        this.getTwoHeightArr();
      });
    },
    getTwoHeightArr() {
      // console.log(" may be empty ", this.$refs.item);
      this.$refs.item.forEach((item) = > {
        this.rightHeightArr.push(item["offsetHeight"]);
      });
      let num = 0;
      this.rightHeightArr.forEach((item) = > {
        num = num + item;
        this.rightHeightSumArr.push(num);
      });
      // Now that we have the height scrollbar, we can bind the scrollevent
      this.bindScrollEvent();
    },
    bindScrollEvent() {
      // Step 4: bind the scroll event to that interval. The idea is to synchronize the left interval with the right interval
      this.$refs.wrapper.onscroll = () = > {
        this.r = this.$refs.wrapper.scrollTop;
        // See where the height of the browser scroll falls, and where it falls, make the corresponding item highlighted
        const scrollWhichIndex = this.rightHeightSumArr.findIndex((item, index) = > {
          return (
            this.r >= this.rightHeightSumArr[index] &&
            this.r < this.rightHeightSumArr[index + 1]); });console.log("Interval",scrollWhichIndex);
        // The initial interval is -1, so it is still the first item, i.e. the index is 0, as the user slides down, so it will
        // Always greater than minus one, so make it plus one to correspond to the highlighted item on the left.
        if (scrollWhichIndex > -1) {
          this.whichIndex = scrollWhichIndex + 1;
        } else {
          this.whichIndex = 0; }}},// Step 5: Scroll the user when they click, because scrolling is associated with highlighting, so as long as you control scrolling, you control highlighting.
    // The scrolling distance is the index of the menu item that the user clicked, and the index is used to find the corresponding item of the cumulative array.
    // That is how far to roll. We have to control the boundary when phi is the first term
    letItemHighLight(i) {
      if (this.rightHeightSumArr[i - 1] = =undefined) {
        this.$refs.wrapper.scrollTop = 0;
      } else {
        this.$refs.wrapper.scrollTop = this.rightHeightSumArr[i - 1]; ,}}}};</script>

<style lang="less" scoped>
#app {
  width: 100%;
  height: 100vh;
  .top {
    width: 100%;
    height: 80px;
    text-align: center;
    line-height: 80px;
    background-color: #e9e9e9;
  }
  .bottom {
    width: 100%;
    height: calc(100% - 80px);
    display: flex;
    .bottomLeft {
      width: 288px;
      height: 100%;
      background-color: #eee;
      .leftItem {
        width: 100%;
        height: 50px;
        line-height: 50px;
        text-align: center;
        cursor: pointer;
      }
      .leftItem:hover {
        background-color: #dfe3f1;
      }
      .highLight {
        background: #dfe3f1; }}.bottomRight {
      width: calc(100% - 288px);
      height: 100%;
      box-sizing: border-box;
      padding: 36px 36px 0 36px;
      overflow-y: auto;
      .bottomRightContent {
        width: 100%;
        box-sizing: border-box;
        padding-bottom: 36px;
        .bottomRightContentHead {
          height: 25px;
          font-family: PingFang SC;
          font-style: normal;
          font-weight: 600;
          font-size: 24px;
          line-height: 25px;
          text-transform: capitalize;
          color: rgba(0.0.0.0.85);
          margin-bottom: 32px;
        }
        .bottomRightContentBody {
          .el-col {
            position: relative;
            margin-bottom: 18px;
            .circle {
              display: inline-block;
              width: 6px;
              height: 6px;
              background: #4677f6;
              border-radius: 50%;
              position: absolute;
              top: 8px;
              left: 0;
            }
            .word {
              margin-left: 12px;
              font-family: PingFang SC;
              font-style: normal;
              font-weight: normal;
              font-size: 14px;
              color: #4677f6;
              cursor: pointer;
            }
            .word:hover {
              text-decoration: underline;
            }
            .topPlace {
              position: absolute;
              top: 1px;
              margin-left: 8px; }}}.bottomRightContentFooter {
          height: 1px;
          width: 100%;
          margin-top: 14px;
          background-color: #e9e9e9;
        }
      }
    }
  }
}
</style>

Copy the code

conclusion

There are many ways to achieve this, I wrote this for reference only. If I write unclear, welcome private letter or article comments. Make progress together with everyone