如何让 Intersection Observer 复制 bootstrap 滚动间谍行为
how to make Intersection Observer to replicate bootstrap scroll spy behavior
我正在构建一个 blazor
应用程序,我应该将 java 脚本代码保持在最低限度。所以我在我的视差页面中只使用 bootstrap css。我已经使用 window.onscroll
使类似滚动间谍的行为起作用,因为它现在不能用 c#
完成;但它的性能很差。
谷歌搜索后,我最近遇到了 IntersectionObserver
API。所以我想使我现有的 window.onscroll
逻辑与 IntersectionObserver
API 一起工作。但是我无法实现它。
这就是我想要做的。在我的带有固定顶部导航栏的视差页面中,当用户滚动到他想查看的相应部分时,我想应用 active
css class 到 a.nav-items
。
这是 HTML:
<body data-spy="scroll" data-target=".site-nav" data-offset="55">
<header id="page-hero" class="site-header d-flex flex-column align-content-between">
<nav class="site-nav family-sans navbar navbar-expand-md navbar-dark fixed-top">
<div class="container-fluid">
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#myTogglerNav" aria-controls="myTogglerNav"
aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand text-uppercase" href="#page-hero">
<i class="fas fa-cube mr-2"></i> Layout Components</a>
<div class="collapse navbar-collapse" id="myTogglerNav">
<div class="navbar-nav ml-auto font-weight-regular text-uppercase">
<a class="nav-item nav-link" href="#page-hero">home</a>
<a class="nav-item nav-link" href="#page-multicolumn">columns</a>
<a class="nav-item nav-link" href="#page-media">media</a>
<a class="nav-item nav-link" href="#page-photogrid">grid</a>
<a class="nav-item nav-link" href="#page-carousel">carousel</a>
<a class="nav-item nav-link" href="#page-nested">nested</a>
<a class="nav-item nav-link" href="#page-icons">icons</a>
<a class="nav-item nav-link" href="#page-floater">floater</a>
<a class="nav-item nav-link" href="#page-cards">cards</a>
</div>
</div>
</div>
</nav>
<section class="layout-hero jumbotron jumbotron-fluid d-flex align-items-center mt-5 text-reverse">
<div class="container text-center">
......
......
......
<article id="page-multicolumn" class="page-section vertical-padding">
<header class="page-section-header container">
.....
.....
.....
<article id="page-media" class="page-section vertical-padding">
<header class="page-section-header container text-center">
....
我有 article
标签,id
匹配顶部 .nav-item
的 a
的 href
。我只粘贴了几个 html,因为所有文章标签的框架都相同。
这是我现有的 window.onscroll
:
var sections = {};
document.querySelectorAll(".page-section,.site-header").forEach(section => sections[section.id] = section.offsetTop);
window.onscroll = function () {
document.querySelector('header nav').classList.toggle('inbody', document.documentElement.scrollTop > 380);
document.querySelector('#page-media .layout-animation').style['visibility'] = 'hidden';
var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
Object.keys(sections).forEach(key => {
if (sections[key] <= scrollPosition + 55) {
document.querySelectorAll('a.nav-item').forEach(a => a.classList.remove('active'));
document.querySelector('a[href="#' + key + '"]').classList.add('active');
if (key === 'page-media') {
document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
}
}
});
};
以上代码有效。但是我想用右边的API.
这是我的 IntersectionObserver
:
if (window.IntersectionObserver) {
let observer = new IntersectionObserver(
(entries, observer) => {
console.log(entries);
entries.forEach(entry => {
/* Here's where we deal with every intersection */
document.querySelectorAll('a.nav-item').forEach(a => a.classList.remove('active'));
if (entry.isIntersecting) {
document.querySelector('header nav').classList.toggle('inbody', entry.target.id !== 'page-hero');
document.querySelector('#page-media .layout-animation').style['visibility'] = 'hidden';
document.querySelector('a[href="#' + entry.target.id + '"]').classList.add('active');
if (entry.target.id === 'page-media') {
document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
}
}
});
}, { root: document.querySelector('.site-nav'), rootMargin: "0px", threshold: 0 });
document.querySelectorAll('.page-section,.site-header').forEach(section => observer.observe(section));
}
但是我的路口观察器不工作。我厌倦了使用 console.log(entry)
看看会发生什么。所有条目仅在页面加载时记录,之后不工作。请协助我哪里错了。
这里的代码不起作用的原因是 运行 在错误的时间。对 document.querySelectorAll('.page-section,.site-header')
的调用需要在这些元素呈现到 DOM 之后执行,否则它不会匹配任何内容。
这是我如何让它工作的,我稍微调整了功能,如下所示,
function highlightMenu() {
const sections = document.querySelectorAll('.page-section,.site-header');
const config = {
rootMargin: '-55px 0px -85%'
};
let observer = new IntersectionObserver(function
(entries, self) {
entries.forEach(entry => {
if (entry.isIntersecting) {
intersectionHandler(entry);
}
});
}, config);
sections.forEach(section => observer.observe(section));
}
function intersectionHandler(entry) {
const id = entry.target.id;
document.querySelector('header nav').classList.toggle('inbody', id !== 'page-hero');
if (id === 'page-media') {
document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
}
const currentlyActive = document.querySelector('nav a.nav-item.active');
const shouldBeActive = document.querySelector('nav a.nav-item[href="#' + id + '"]');
if (currentlyActive) {
currentlyActive.classList.remove('active');
}
if (shouldBeActive) {
shouldBeActive.classList.add('active');
}
}
并使用 Blazor 中的 JS 互操作从我的组件内部调用 OnAfterRenderAsync
,
@code{
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("highlightMenu");
}
}
}
我正在构建一个 blazor
应用程序,我应该将 java 脚本代码保持在最低限度。所以我在我的视差页面中只使用 bootstrap css。我已经使用 window.onscroll
使类似滚动间谍的行为起作用,因为它现在不能用 c#
完成;但它的性能很差。
谷歌搜索后,我最近遇到了 IntersectionObserver
API。所以我想使我现有的 window.onscroll
逻辑与 IntersectionObserver
API 一起工作。但是我无法实现它。
这就是我想要做的。在我的带有固定顶部导航栏的视差页面中,当用户滚动到他想查看的相应部分时,我想应用 active
css class 到 a.nav-items
。
这是 HTML:
<body data-spy="scroll" data-target=".site-nav" data-offset="55">
<header id="page-hero" class="site-header d-flex flex-column align-content-between">
<nav class="site-nav family-sans navbar navbar-expand-md navbar-dark fixed-top">
<div class="container-fluid">
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#myTogglerNav" aria-controls="myTogglerNav"
aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand text-uppercase" href="#page-hero">
<i class="fas fa-cube mr-2"></i> Layout Components</a>
<div class="collapse navbar-collapse" id="myTogglerNav">
<div class="navbar-nav ml-auto font-weight-regular text-uppercase">
<a class="nav-item nav-link" href="#page-hero">home</a>
<a class="nav-item nav-link" href="#page-multicolumn">columns</a>
<a class="nav-item nav-link" href="#page-media">media</a>
<a class="nav-item nav-link" href="#page-photogrid">grid</a>
<a class="nav-item nav-link" href="#page-carousel">carousel</a>
<a class="nav-item nav-link" href="#page-nested">nested</a>
<a class="nav-item nav-link" href="#page-icons">icons</a>
<a class="nav-item nav-link" href="#page-floater">floater</a>
<a class="nav-item nav-link" href="#page-cards">cards</a>
</div>
</div>
</div>
</nav>
<section class="layout-hero jumbotron jumbotron-fluid d-flex align-items-center mt-5 text-reverse">
<div class="container text-center">
......
......
......
<article id="page-multicolumn" class="page-section vertical-padding">
<header class="page-section-header container">
.....
.....
.....
<article id="page-media" class="page-section vertical-padding">
<header class="page-section-header container text-center">
....
我有 article
标签,id
匹配顶部 .nav-item
的 a
的 href
。我只粘贴了几个 html,因为所有文章标签的框架都相同。
这是我现有的 window.onscroll
:
var sections = {};
document.querySelectorAll(".page-section,.site-header").forEach(section => sections[section.id] = section.offsetTop);
window.onscroll = function () {
document.querySelector('header nav').classList.toggle('inbody', document.documentElement.scrollTop > 380);
document.querySelector('#page-media .layout-animation').style['visibility'] = 'hidden';
var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
Object.keys(sections).forEach(key => {
if (sections[key] <= scrollPosition + 55) {
document.querySelectorAll('a.nav-item').forEach(a => a.classList.remove('active'));
document.querySelector('a[href="#' + key + '"]').classList.add('active');
if (key === 'page-media') {
document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
}
}
});
};
以上代码有效。但是我想用右边的API.
这是我的 IntersectionObserver
:
if (window.IntersectionObserver) {
let observer = new IntersectionObserver(
(entries, observer) => {
console.log(entries);
entries.forEach(entry => {
/* Here's where we deal with every intersection */
document.querySelectorAll('a.nav-item').forEach(a => a.classList.remove('active'));
if (entry.isIntersecting) {
document.querySelector('header nav').classList.toggle('inbody', entry.target.id !== 'page-hero');
document.querySelector('#page-media .layout-animation').style['visibility'] = 'hidden';
document.querySelector('a[href="#' + entry.target.id + '"]').classList.add('active');
if (entry.target.id === 'page-media') {
document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
}
}
});
}, { root: document.querySelector('.site-nav'), rootMargin: "0px", threshold: 0 });
document.querySelectorAll('.page-section,.site-header').forEach(section => observer.observe(section));
}
但是我的路口观察器不工作。我厌倦了使用 console.log(entry)
看看会发生什么。所有条目仅在页面加载时记录,之后不工作。请协助我哪里错了。
这里的代码不起作用的原因是 运行 在错误的时间。对 document.querySelectorAll('.page-section,.site-header')
的调用需要在这些元素呈现到 DOM 之后执行,否则它不会匹配任何内容。
这是我如何让它工作的,我稍微调整了功能,如下所示,
function highlightMenu() {
const sections = document.querySelectorAll('.page-section,.site-header');
const config = {
rootMargin: '-55px 0px -85%'
};
let observer = new IntersectionObserver(function
(entries, self) {
entries.forEach(entry => {
if (entry.isIntersecting) {
intersectionHandler(entry);
}
});
}, config);
sections.forEach(section => observer.observe(section));
}
function intersectionHandler(entry) {
const id = entry.target.id;
document.querySelector('header nav').classList.toggle('inbody', id !== 'page-hero');
if (id === 'page-media') {
document.querySelector('#page-media .layout-animation').classList.add('animated', 'fadeInRight');
}
const currentlyActive = document.querySelector('nav a.nav-item.active');
const shouldBeActive = document.querySelector('nav a.nav-item[href="#' + id + '"]');
if (currentlyActive) {
currentlyActive.classList.remove('active');
}
if (shouldBeActive) {
shouldBeActive.classList.add('active');
}
}
并使用 Blazor 中的 JS 互操作从我的组件内部调用 OnAfterRenderAsync
,
@code{
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("highlightMenu");
}
}
}