页面锚点使用;__vueParentComponent 和 ctx使用

文章描述了一个使用Vue.js编写的组件,展示如何在页面中实现点击标题时滚动到相应锚点的平滑滚动效果,涉及DOM操作和滚动事件处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.正常联动锚点

效果图:
在这里插入图片描述

以下vue2代码直接复制使用

<template>
  <div style="height: 100%;">
    <div style="display: flex;height: 100%;">
      <div style="width: 200px;border: 1px solid pink;">
        <div v-for="(item, index) in titleList" :key="index" style="margin-top: 10px;cursor: pointer;"
          :style="{ backgroundColor: activeAnchor === item.dataKey ? 'cyan' : '#fff' }"
          @click="() => scrollToAnchor(item.dataKey)">
          {{ item.label }}
        </div>
      </div>

      <!-- 这个大集合需要设置滚动 overflow: hidden;overflow-y: auto; -->
      <div ref="scrollContainer" id="scrollContainer" style="flex: 1;overflow: hidden;overflow-y: auto;" @scroll="handleScroll">
        <div ref="title1" id="title1" style="height: 200px; background-color: #c1f0de;">1</div>
        <div ref="title2" id="title2" style="height: 600px; background-color: #c1f0de;margin-top: 10px;">2</div>
        <div ref="title3" id="title3" style="height: 500px; background-color: #c1f0de;margin-top: 10px;">3</div>
        <div ref="title4" id="title4" style="height: 200px; background-color: #c1f0de;margin-top: 10px;">4</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeAnchor: 'title1',//当前选中锚点
      isScrollingToAnchor: false, //是否滚动中
      titleList: [
        { dataKey: 'title1', label: '标题1' },
        { dataKey: 'title2', label: '标题2' },
        { dataKey: 'title3', label: '标题3' },
        { dataKey: 'title4', label: '标题4' },
      ]
    }
  },
  methods: {
    // 滚动监听 当前所展示的锚点
    handleScroll() {
      // 如果正在滚动到锚点,则不再触发新的滚动操作
      if (this.isScrollingToAnchor) return false

      const containerElement = document.getElementById('scrollContainer')
      if (!containerElement) return

      if (this.titleList && this.titleList.length) {
        for (let i = 0; i < this.titleList.length; i++) {
          const dataKey = this.titleList[i].dataKey
          console.log('dataKey', dataKey, containerElement)
          const anchorElement = document.getElementById(dataKey)
          if (!anchorElement || anchorElement.length < 0) return

          // getBoundingClientRect()方法返回一个DOMRect对象,该对象提供了元素的大小及其相对于视口的位置信息。这个DOMRect对象包含了元素的位置、宽度、高度和其他相关信息
          const containerRect = containerElement.getBoundingClientRect()
          // const anchorRect = anchorElement[0].getBoundingClientRect()
          const anchorRect = anchorElement.getBoundingClientRect()
          console.log(containerRect, anchorRect, dataKey)

          // 判断锚点是否在滚动容器的可见区域内
          const isInViewport =
            anchorRect.top >= containerRect.top &&
            anchorRect.bottom <= containerRect.bottom

          if (isInViewport) {
            this.activeAnchor = dataKey
            return false
          }
        }
      }
    },
    // 滚动锚点
    scrollToAnchor(dataKey) {
      const anchorElement = document.getElementById(dataKey)

      // 使用原生JavaScript滚动到锚点位置
      if (anchorElement) {
        this.isScrollingToAnchor = true

        anchorElement.scrollIntoView({
          behavior: 'smooth', // 添加平滑滚动效果,可选
          block: 'start' // 控制垂直方向的对齐方式,可选值有 'start', 'center', 'end', 'nearest'
        })

        this.activeAnchor = dataKey

        // 滚动完成后将标志位复位
        setTimeout(() => {
          this.isScrollingToAnchor = false
        }, 3000) // 3000 毫秒是滚动完成的估计时间,可以根据实际情况调整
      }
    }
  },
}
</script>

<style></style>

2.div使用prop自定义属性锚点跳转:

效果图:
在这里插入图片描述

以下vue3代码可直接复制

<template>
  <div>
    <input v-model="targetProp" type="number" placeholder="Enter a number between 1 and 10" />
    <button @click="scrollToDiv">Scroll to Div</button>
    <div class="container">
      <!-- <div v-for="number in numbers" :key="number" :id="'box-' + number" :prop="number" class="box">
        Div {{ number }}
      </div> -->
      <div prop="1" class="box">1</div>
      <div prop="2" class="box">2</div>
      <div prop="3" class="box">3</div>
      <div prop="4" class="box">4</div>
      <div prop="5" class="box">5</div>
      <div prop="6" class="box">6</div>
      <div prop="7" class="box">7</div>
      <div prop="8" class="box">8</div>
      <div prop="9" class="box">9</div>
      <div prop="10" class="box">10</div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const numbers = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const targetProp = ref('');

const scrollToDiv = () => {
  const targetValue = targetProp.value;
  // const targetDiv = document.getElementById(`box-${targetValue}`) // 同等上方注释的 使用id锚点跳转
  const targetDiv = document.querySelector(`[prop="${targetValue}"]`) // 使用prop自定义属性跳转
  if (targetDiv) {
    targetDiv.scrollIntoView({ behavior: 'smooth' });
  }
};
</script>

<style>
.container {
  height: 500px;
  overflow-y: scroll;
  border: 1px solid #ccc;
}

.box {
  width: 200px;
  height: 200px;
  margin: 10px;
  background-color: #f0f0f0;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 1px solid #ddd;
}
</style>

3.通过前端组件库 Vue 组件实例上的自定义属性跳转

例如通过el-form-item组件标签上的prop属性来锚点跳转

效果图:
在这里插入图片描述

重点介绍:

1.由于 el.getAttribute(‘prop’) 得到的值是 null,这是因为 prop 属性在 el-form-item 中是通过 Vue 绑定的,不会直接在 DOM 中作为属性存在。我们需要通过 Vue 的实例来访问这些绑定的属性。__vueParentComponent 和 ctx 就是其中的两个内部属性。

以下vue3代码可直接复制:

<template>
  <div>
    <el-input v-model="targetProp" placeholder="输入el-form-item的prop值" clearable />
    <el-button type="primary" @click="scrollToFormItem">Scroll to Form Item</el-button>
    <div style="height: 1000px;border: 1px solid red;">1</div>
    <el-row>
      <el-col :span="12">
        <el-form-item :label="'厂商负责人'" prop="manufactureOwner">
          <el-input placeholder="请输入" clearable />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item :label="'厂商'" prop="manufactureName">
          <el-input placeholder="请输入" clearable />
        </el-form-item>
      </el-col>
      <el-col :span="24">
        <div style="height: 1000px;border: 1px solid red;">1</div>
      </el-col>
      <el-col :span="12">
        <el-form-item :label="'厂商负责人电话'" prop="manufactureOwnerPhone">
          <span class="ml">1223</span>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item :label="'厂商工作人员'" prop="manufactureWorker">
          <span class="ml">444</span>
        </el-form-item>
      </el-col>
    </el-row>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { ElInput, ElButton, ElRow, ElCol, ElFormItem } from 'element-plus';
import 'element-plus/dist/index.css';

const targetProp = ref('');
const formItems = ref([]);

onMounted(() => {
  formItems.value = document.querySelectorAll('.el-form-item');
});

const scrollToFormItem = () => {
  const targetElement = Array.from(formItems.value).find((el) => {
    console.log(el, el.__vueParentComponent, el.__vueParentComponent.ctx);
    const instance = el.__vueParentComponent.ctx;
    return instance.prop === targetProp.value;
  });
  console.log(targetElement);

  if (targetElement) {
    targetElement.scrollIntoView({ behavior: 'smooth' });
  }
};
</script>

<style>
.ml {
  margin-left: 10px;
}
</style>

4.Vue 组件实例和内部属性

在 Vue 中,每个组件实例都有一些内部属性和方法,这些属性和方法不在 Vue 的文档中公开,但它们是 Vue 内部工作的一部分。__vueParentComponent 和 ctx 就是其中的两个内部属性。

__vueParentComponent:

  • 作用: __vueParentComponent 是 Vue 组件实例中的一个内部属性,指向该组件的父组件实例。

  • 用途: 它主要用于内部组件树的管理,使 Vue 能够正确地管理父子组件之间的关系。

ctx:

  • 作用: ctx 是 Vue 组件实例中的一个内部属性,代表该组件的上下文(context)。
  • 用途: ctx 包含了组件的所有公开属性和方法,包括 props、data、computed、methods 等。

代码解释
以下是关键代码:

const instance = el.__vueParentComponent.ctx;
  • el: 这是一个 HTML 元素,通常是通过
  • document.querySelectorAll 或其他 DOM 查询方法获得的。
  • el.__vueParentComponent: 这是指向包含 el 的 Vue 组件实例。
  • el.__vueParentComponent.ctx: 这是指向该 Vue 组件实例的上下文,在这个上下文中你可以访问该组件的所有公开属性和方法,包括 props。

5.如何使用 __vueParentComponent 和 ctx 实现功能

我们需要通过 prop 属性值找到对应的 <el-form-item>,并滚动到该元素的位置。因为 prop 属性在 DOM 中不可见,所以我们需要访问 Vue 组件实例来获取这个值。

具体实现如下:

const targetElement = Array.from(formItems.value).find((el) => {
  const instance = el.__vueParentComponent.ctx;
  return instance.prop === targetProp.value;
});
  • Array.from(formItems.value): 将 formItems 中的 NodeList 转换为数组,以便可以使用 find 方法。
  • el.__vueParentComponent.ctx: 访问包含该元素的 Vue 组件实例的上下文。
  • instance.prop: 获取该组件实例的 prop 属性值。
  • return instance.prop === targetProp.value: 查找 prop 属性值等于用户输入的 targetProp.value 的组件实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值