异常问题
页面通过描点链接进入锚点区域(html使用了scroll-behavior: smooth;
),在页面滚动触发IntersectionObserver
对页面内容的显示区域监听,去导航栏中滚动到指定位置。
步骤:
- 点击锚点
- 页面滚动到锚点位置(此时2、3、4步骤应该一直触发)
- 触发监听
- 滚动导航栏区域
当执行到2.3.4时页面滚动出现异常。
问题
于 通义千问 查询得知。页面滚动(锚点+scroll-behavior: smooth;
)和其他div滚动出现冲突。
问题代码
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html {
scroll-behavior: smooth;
transition: 0.2s;
}
.header {
width: 200px;
height: 50vh;
overflow: auto;
position: fixed;
display: flex;
flex-direction: column;
}
.main {
width: fit-content;
margin: 0 auto;
}
.main > div {
width: 300px;
border-bottom: 1px solid darkgrey;
padding: 10px;
margin: 10px;
}
</style>
</head>
<body>
<div class="header"></div>
<div class="main"></div>
</body>
<script async>
var c = -1;
var max = 50;
var s = [];
while (++c < max) {
var aa = document.createElement("a");
aa.innerText = "0x" + c.toString(16);
aa.id = "header" + c;
aa.href = "#id" + c;
document.querySelector(".header").appendChild(aa);
var aaa = document.createElement("div");
aaa.innerText = c.toString(16);
aaa.id = "id" + c;
aaa.style.height = (c + 3) * 5 + "px";
document.querySelector(".main").append(aaa);
}
var show = [];
const observer = new IntersectionObserver(entries => {
entries.filter((value) => {
if (value.isIntersecting) {
show.push(value.target);
} else {
const index = show.findIndex(e => e === value.target);
if (index !== -1) {
show.splice(index, 1);
}
}
});
if (show.length > 0) {
const indexes = show.map(e => Array.from(e.parentNode.children).findIndex(f => f === e)).sort((a, b) => a - b);
const headerDomShow = document.querySelector(".header").children.item(indexes[0]);
document.querySelector(".header").scrollTop = headerDomShow.offsetTop;
}
});
document.querySelectorAll(".main div").forEach(e => {
observer.observe(e);
});
</script>
</html>
解决方案:
删除scroll-behavior: smooth;
。
此方案可以解决但是会出现页面滚动无滑动的效果。
所以在此操作上的解决方案:
- 删除
scroll-behavior: smooth;
- 删除锚点
- 都使用scrollTop来滚动
- 滚动时通过js来控制平滑滚动
改动后代码如下:(仅一个简单示例)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html {
scroll-behavior: auto;
transition: 0.2s;
}
.header {
width: 200px;
height: 50vh;
overflow: auto;
position: fixed;
display: flex;
flex-direction: column;
}
.main {
width: fit-content;
margin: 0 auto;
}
.main > div {
width: 300px;
border-bottom: 1px solid darkgrey;
padding: 10px;
margin: 10px;
}
</style>
</head>
<body>
<div class="header"></div>
<div class="main"></div>
</body>
<script async>
var c = -1;
var max = 50;
var s = [];
// 创建一些组件扔在页面上
while (++c < max) {
const val = "0x" + c.toString(16);
var aa = document.createElement("div");
aa.innerText = val
aa.id = "header" + val;
document.querySelector(".header").appendChild(aa);
var aaa = document.createElement("div");
aaa.innerText = val;
aaa.id = "id" + val;
aaa.style.height = (c + 3) * 5 + "px";
document.querySelector(".main").append(aaa);
}
var show = [];
// 添加导航的点击事件,进行html的滚动
document.querySelector(".header").addEventListener("click", function (e) {
if (e.target)
scrollBody(document.getElementById(e.target.id.replace("header", "id")));
})
// 平滑滚动到指定位置
const smoothScrollTo = (element, targetPosition, duration) => {
const start = element.scrollTop;
const startTime = performance.now();
const easing = t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
const animationFrame = () => {
const now = performance.now();
const timeElapsed = now - startTime;
const progress = Math.min(timeElapsed / duration, 1);
element.scrollTop = easing(progress) * (targetPosition - start) + start;
if (progress < 1) {
requestAnimationFrame(animationFrame);
}
};
requestAnimationFrame(animationFrame);
};
// html滚动到指定标签位置
function scrollBody(target) {
const header = document.documentElement;
smoothScrollTo(header, target.offsetTop, 200);
}
// 监听控件的显示、隐藏
const observer = new IntersectionObserver(entries => {
entries.filter((value) => {
if (value.isIntersecting) {
show.push(value.target);
} else {
const index = show.findIndex(e => e === value.target);
if (index !== -1) {
show.splice(index, 1);
}
}
});
if (show.length > 0) {
const indexes = show.map(e => Array.from(e.parentNode.children).findIndex(f => f === e)).sort((a, b) => a - b);
const headerDomShow = document.querySelector(".header").children.item(indexes[0]);
const targetScrollTop = headerDomShow.offsetTop;
smoothScrollTo(document.querySelector(".header"), targetScrollTop, 200);
}
});
document.querySelectorAll(".main div").forEach(e => {
observer.observe(e);
});
</script>
</html>