react Anchor 不同页面之间实现锚点
- 一、定义
- 二、使用步骤
- 三、开发流程
- (一)、组件
- (二)、页面布局
- (三)、点击事件
- (四)、总结说明
- 一、react单页面应用,当前页面的锚点
- 二、react单页面应用,不同页面的锚点
- 思路:锚点只能在当前页面使用,所以用useEffect()拦截
- 三、总结
- 五、其它
- 解决办法:加上location.hash可以解决
一、定义
Anchor锚点是:用于跳转到页面指定位置。
何时使用
需要展现当前页面上可供跳转的锚点链接,以及快速在锚点之间跳转。
二、使用步骤
1、首先在react项目中引用antd的锚点
import {Anchor} from 'antd';
const { Link } = Anchor;
2、发现页面进行跳转而不是点位到页面的锚点,发现url有所改变
浏览器支持的锚点必须是通过hash来实现的,
三、开发流程
(一)、组件
(二)、页面布局
onClick()点击事件
<Anchor onClick={onAnchorChange}>
<div className={styles.digUlItem}>
{item.des.map((littleItem, littleIndex) => {
return (
<Link
key={littleItem}
href={`#${littleItem}`}
title={littleItem}
></Link>
);
})}
</div>
</Anchor>
(三)、点击事件
const onAnchorChange = (e,link) => {
e.preventDefault();
console.log('ddd e anchor>>>', e);
console.log('ddd hash anchor>>>', link);
};
(四)、总结说明
一、react单页面应用,当前页面的锚点
简单使用
- antd中Anchor组件阻止默认路由跳转
- 使用Anchor组件时,添加click方法:
1、阻止默认事件 e.preventDefault();阻止link的href跳转路由事件;
2、使用H5的scrollToAnchor添加页面滚动效果:
当前页面实现锚点,的点击事件,代码如下:
链接: react+antd中Anchor锚点踩坑===当前页面跳转,阻止点击事件的默认事件
handleClickFun= (e, link) => {
e.preventDefault();
if (link.href) {
// 找到锚点对应得的节点
let element = document.getElementById(link.href);
// 如果对应id的锚点存在,就跳滚动到锚点顶部
element && element .scrollIntoView({block:'start', behavior:'smooth'});
}
};
// block:表示垂直方向的滚动对齐方式,“start”, “center”, “end”, 或 “nearest”
// behavior:表示动画效果,auto/smooth(滚动效果)
使用加强版
- 点击事件阻止a标签的默认
e.preventDefault(); - 原理:
【antdesign中anchor点击锚点后手动刷新页面,显示空白】是因为antdesign anchor底层代码是使用a标签来进行锚点跳转的,所以我们需要阻止a标签的默认行为。
antd Anchor底层是使用的a标签实现锚点功能的,而a标签的默认行为就是路由跳转(跳转到href),只要阻止a标签的默认行为就可以了。
-
控制台打印效果,比较如下:
-
阻止默认事件前:
-
阻止默认事件后:
二、react单页面应用,不同页面的锚点
思路:锚点只能在当前页面使用,所以用useEffect()拦截
- 详细思路:我在项目中使用的锚点并不是真正意义上的锚点,只是利用了点击a便签会在浏览器的地址栏url出现#后的参数,可以理解为vue中的路由传参query。
通过react的函数组件的钩子函数useEffect()。通过useEffect(handleFun,[第二个参数])的第二个参数对浏览器的地址栏进行拦截。我拦截的是:location.hash 对#号后的部分进行拦截。这里而外说一句:location对象,他是window对象和document对象的属性,它表示载入窗口的url,它可以解析url。 - 代码如下:
useEffect(() => {
let myTimer
clearTimeout(myTimer);
if (!!location.hash) {
const scrollToAnchor = (anchorName) => {
if (anchorName) {
let anchorElement = document.getElementById(anchorName);
console.log('element>>>', anchorElement)
if (anchorElement !== null) {
let scrollHeight = anchorElement.offsetTop - 65;
if (!!scrollHeight) {
myTimer = setTimeout(() => {
window.scrollTo({
left: 0, //x轴
top: scrollHeight, //y轴
behavior: 'smooth'
})
}, 500);
//useEffect()中清除定时器,return出去就可以了。
return () => clearTimeout(myTimer)
} else {
return () => clearTimeout(myTimer)
}
}
}
}
scrollToAnchor(location.hash)
}
}, [location.hash])
useEffect(() => {
console.log('location.hash>>>', location.hash);
if (!!location.hash) {
const scrollToAnchor = (anchorName) => {
console.log('anchorName>>>', anchorName)
if (anchorName) {
let anchorElement = document.getElementById(anchorName);
console.log('element>>>', anchorElement)
if (anchorElement !== null) {
let scrollHeight = anchorElement.offsetTop - 65;
console.log('anchorElement.offsetTop>>>', anchorElement.offsetTop)
console.log('scrollHeight>>>', scrollHeight)
let myTimer
if (!!scrollHeight) {
myTimer = setTimeout(() => {
console.log('定时器>>>');
window.scrollTo({
left: 0, //x轴
top: scrollHeight, //y轴
behavior: 'smooth'
})
}, 500);
//useEffect()中清除定时器,return出去就可以了。
return () => clearTimeout(myTimer)
} else {
//useEffect()中清除定时器,return出去就可以了。
return () => clearTimeout(myTimer)
}
}
}
}
scrollToAnchor(location.hash)
}
}, [location.hash])
- 完整代码:
import React,{useEffect} from 'react';
import styles from './index.less';
import { Image, Space } from 'antd';
import FarmServerList from '@/components/productServer/FarmServerList';
import farm1 from '@/assets/productServer/farm1.png';
export default function index({ content = [] }) {
useEffect(() => {
console.log('location.hash>>>', location.hash);
if (!!location.hash) {
const scrollToAnchor = (anchorName) => {
console.log('anchorName>>>', anchorName)
if (anchorName) {
let anchorElement = document.getElementById(anchorName);
console.log('element>>>', anchorElement)
if (anchorElement !== null) {
let scrollHeight = anchorElement.offsetTop - 65;
console.log('anchorElement.offsetTop>>>', anchorElement.offsetTop)
console.log('scrollHeight>>>', scrollHeight)
let myTimer
if (!!scrollHeight) {
myTimer = setTimeout(() => {
console.log('定时器>>>');
window.scrollTo({
left: 0, //x轴
top: scrollHeight, //y轴
behavior: 'smooth'
})
}, 500);
//useEffect()中清除定时器,return出去就可以了。
return () => clearTimeout(myTimer)
} else {
//useEffect()中清除定时器,return出去就可以了。
return () => clearTimeout(myTimer)
}
}
}
}
scrollToAnchor(location.hash)
}
}, [location.hash])
return (
<div className={styles.home_box}>
{content.map((item,index) => {
if (item.id % 2 == 0) {
return (
<div className={styles.content} key={item.id}>
<div className={styles.left}>
<Space direction={'vertical'} size={'large'}>
<span className={styles.span1} id={`#${index}`}>
{item.title}
</span>
<FarmServerList list={item.list} align={item.id % 2} />
</Space>
</div>
<div className={styles.right}>
<Image src={item.img} preview={{ mask: false }} />
</div>
</div>
);
} else {
return (
<div className={styles.content} key={item.id}>
<div className={styles.left}>
<Image src={item.img} preview={{ mask: false }} />
</div>
<div className={styles.right}>
<Space direction={'vertical'} size={'large'}>
<span className={styles.span} id={`#${index}`}>{item.title}</span>
<FarmServerList list={item.list} />
</Space>
</div>
</div>
);
}
})}
</div>
);
}
三、总结
实际效果:能够实现从一个页面跳转到另外一个页面,并且根据浏览器地址栏location.hash获取到#号后的内容,从一个页面的锚点->点击->跳转到另一个页面->根据getElementById获取真实节点->windowm.scrollTo页面滚动
BUG点:
现象:点击第一次跳转不能滚动,重新第二次跳转能够实现滚动。
原因分析:react框架有虚拟Dom,在useEffect不能获取到真实的Dom节点,因为页面组件的useEffect会在浏览器加载一个页面渲染的时候调用useEffect()两次,第一次拿不到,第二次能拿到节点。
但我们想要第一节进入页面就实现滚动
所以:考虑使用react的函数组件的另一个狗子函数useRef()
链接: useEffect执行时机
链接: useRef()操作真实dom节点
五、其它
解决办法:加上location.hash可以解决
浏览器支持的锚点必须是通过hash来实现的,
不行的话就不能用锚点的形式,只能自己写个组件,注册点击事件,然后获取要滚动到的元素的位置,设置window的scrollTop
React不引入antd如何实现锚点跳转
代码如下:
scrollToAnchor (id){
document.getElementById(id).scrollIntoView(false);
}
render:
-
this.scrollToAnchor(‘Summarize’)}>Summarize
-
this.scrollToAnchor(‘ProductFunction’)}>ProductFunction
-
this.scrollToAnchor(‘ToHelpAnswer’)}>ToHelpAnswer