Skip to content

Commit 000acaa

Browse files
authored
Merge 3f7bff1 into c658b80
2 parents c658b80 + 3f7bff1 commit 000acaa

File tree

3 files changed

+168
-22
lines changed

3 files changed

+168
-22
lines changed

.changeset/every-maps-itch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@patternfly/pfe-core": patch
3+
---
4+
`ScrollSpyController`: improve responsiveness of scroll spy
5+

core/pfe-core/controllers/scroll-spy-controller.ts

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@ export class ScrollSpyController implements ReactiveController {
3737
static {
3838
if (!isServer) {
3939
addEventListener('scroll', () => {
40-
if (Math.round(window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
41-
this.#instances.forEach(ssc => {
42-
ssc.#setActive(ssc.#linkChildren.at(-1));
43-
});
44-
}
40+
this.#instances.forEach(ssc => {
41+
ssc.#reconcile();
42+
});
43+
}, { passive: true });
44+
addEventListener('scrollend', () => {
45+
this.#instances.forEach(ssc => {
46+
ssc.#reconcile();
47+
});
4548
}, { passive: true });
4649
addEventListener('hashchange', () => {
4750
this.#instances.forEach(ssc => {
@@ -72,9 +75,10 @@ export class ScrollSpyController implements ReactiveController {
7275

7376
#threshold: number | number[];
7477

75-
#intersectingTargets = new Set<Element>();
78+
#intersectionEntries = new Set<IntersectionObserverEntry>();
7679

7780
#linkTargetMap = new Map<Element, Element | null>();
81+
#targetLinkMap = new Map<Element, Element | null>();
7882

7983
#getRootNode: () => Node | null;
8084

@@ -148,6 +152,24 @@ export class ScrollSpyController implements ReactiveController {
148152

149153
#initializing = true;
150154

155+
#reconcile() {
156+
const { scrollY, innerHeight } = window;
157+
let link: Element | null | undefined = null;
158+
if (scrollY === 0) {
159+
link = this.#linkChildren.at(0);
160+
} else if (Math.round(innerHeight + scrollY) >= document.body.scrollHeight) {
161+
link = this.#linkChildren.at(-1);
162+
} else {
163+
const [entry] = [...this.#intersectionEntries].sort((a, b) => {
164+
return b.boundingClientRect.y - a.boundingClientRect.y;
165+
});
166+
link = this.#targetLinkMap.get(entry?.target);
167+
}
168+
if (link) {
169+
this.#setActive(link);
170+
}
171+
}
172+
151173
async #initIo() {
152174
const rootNode = this.#getRootNode();
153175
if (rootNode instanceof Document || rootNode instanceof ShadowRoot) {
@@ -160,6 +182,7 @@ export class ScrollSpyController implements ReactiveController {
160182
if (target) {
161183
this.#io?.observe(target);
162184
this.#linkTargetMap.set(link, target);
185+
this.#targetLinkMap.set(target, link);
163186
}
164187
}
165188
}
@@ -209,32 +232,19 @@ export class ScrollSpyController implements ReactiveController {
209232
this.#markPassed(link, boundingClientRect.top < intersectionRect.top);
210233
}
211234
}
212-
const link = [...this.#passedLinks];
213-
const last = link.at(-1);
214-
this.#setActive(last ?? this.#linkChildren.at(0));
215235
}
216236
this.#intersected = true;
217-
this.#intersectingTargets.clear();
237+
this.#intersectionEntries.clear();
218238
for (const entry of entries) {
219239
if (entry.isIntersecting) {
220-
this.#intersectingTargets.add(entry.target);
240+
this.#intersectionEntries.add(entry);
221241
}
222242
}
223243
if (this.#initializing) {
224-
const ints = entries?.filter(x => x.isIntersecting) ?? [];
225-
if (this.#intersectingTargets.size > 0) {
226-
const [{ target = null } = {}] = ints;
227-
const { id } = target ?? {};
228-
if (id) {
229-
const link = this.#linkChildren.find(link => this.#getHash(link) === `#${id}`);
230-
if (link) {
231-
this.#setActive(link);
232-
}
233-
}
234-
}
235244
this.#initializing = false;
236245
}
237246
this.#onIntersection?.();
247+
this.#reconcile();
238248
}
239249

240250
/**
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<article>
2+
<div class="content">
3+
<section id="chapter-1">
4+
<h1>Chapter 1</h1>
5+
</section>
6+
<section id="chapter-2">
7+
<h2>
8+
<a href="#chapter-2">Chapter 2</a>
9+
</h2>
10+
</section>
11+
<section id="chapter-3">
12+
<h2>
13+
<a href="#chapter-3">Chapter 3</a>
14+
</h2>
15+
<section id="sub-chapter-1">
16+
<h3>
17+
<a href="#sub-chapter-1">Sub-chapter 3.1</a>
18+
</h3>
19+
</section>
20+
<section id="sub-chapter-2">
21+
<h3>
22+
<a href="#sub-chapter-2">Sub-chapter 3.2</a>
23+
</h3>
24+
</section>
25+
<section id="sub-chapter-3">
26+
<h3>
27+
<a href="#sub-chapter-3">Sub-chapter 3.3</a>
28+
</h3>
29+
</section>
30+
<section id="sub-chapter-4">
31+
<h3>
32+
<a href="#sub-chapter-4">Sub-chapter 3.4</a>
33+
</h3>
34+
</section>
35+
<section id="sub-chapter-5">
36+
<h3>
37+
<a href="#sub-chapter-5">Sub-chapter 3.5</a>
38+
</h3>
39+
</section>
40+
<section id="sub-chapter-6">
41+
<h3>
42+
<a href="#sub-chapter-6">Sub-chapter 3.6</a>
43+
</h3>
44+
</section>
45+
<section id="sub-chapter-7">
46+
<h3>
47+
<a href="#sub-chapter-7">Sub-chapter 3.7</a>
48+
</h3>
49+
</section>
50+
<section id="sub-chapter-8">
51+
<h3>
52+
<a href="#sub-chapter-8">Sub-chapter 3.8</a>
53+
</h3>
54+
</section>
55+
<section id="sub-chapter-9">
56+
<h3>
57+
<a href="#sub-chapter-9">sub-chapter 3.9</a>
58+
</h3>
59+
</section>
60+
<section id="sub-chapter-10">
61+
<h3>
62+
<a href="#sub-chapter-10">Sub-chapter 3.10</a>
63+
</h3>
64+
</section>
65+
</section>
66+
<section id="chapter-4">
67+
<h2>
68+
<a href="#chapter-4">Chapter 4</a>
69+
</h2>
70+
</section>
71+
<section id="chapter-5">
72+
<h2>
73+
<a href="#chapter-5">Chapter 5</a>
74+
</h2>
75+
<section id="sub-chapter-11">
76+
<h3>
77+
<a href="#sub-chapter-11">Sub-chapter 5.1</a>
78+
</h3>
79+
</section>
80+
<section id="sub-chapter-12">
81+
<h3>
82+
<a href="#sub-chapter-12">Sub-chapter 5.2</a>
83+
</h3>
84+
</section>
85+
</section>
86+
</div>
87+
<pf-jump-links vertical>
88+
<pf-jump-links-item href="#sub-chapter-1">Sub-chapter 3.1</pf-jump-links-item>
89+
<pf-jump-links-item href="#sub-chapter-2">Sub-chapter 3.2</pf-jump-links-item>
90+
<pf-jump-links-item href="#sub-chapter-3">Sub-chapter 3.3</pf-jump-links-item>
91+
<pf-jump-links-item href="#sub-chapter-4">Sub-chapter 3.4</pf-jump-links-item>
92+
<pf-jump-links-item href="#sub-chapter-5">Sub-chapter 3.5</pf-jump-links-item>
93+
<pf-jump-links-item href="#sub-chapter-6">Sub-chapter 3.6</pf-jump-links-item>
94+
<pf-jump-links-item href="#sub-chapter-7">Sub-chapter 3.7</pf-jump-links-item>
95+
<pf-jump-links-item href="#sub-chapter-8">Sub-chapter 3.8</pf-jump-links-item>
96+
<pf-jump-links-item href="#sub-chapter-9">Sub-chapter 3.9</pf-jump-links-item>
97+
<pf-jump-links-item href="#sub-chapter-10">Sub-chapter 3.10</pf-jump-links-item>
98+
<pf-jump-links-item href="#sub-chapter-11">Sub-chapter 5.1</pf-jump-links-item>
99+
<pf-jump-links-item href="#sub-chapter-12">Sub-chapter 5.2</pf-jump-links-item>
100+
</pf-jump-links>
101+
</article>
102+
103+
104+
<script type="module">
105+
import '@patternfly/elements/pf-jump-links/pf-jump-links.js';
106+
</script>
107+
108+
<style>
109+
article {
110+
display: grid;
111+
grid-template-columns: 1fr 0.25fr;
112+
gap: var(--pf-global--spacer--md, 1rem);
113+
}
114+
115+
.content {
116+
min-height: 100dvh;
117+
}
118+
119+
section {
120+
border: var(--pf-global--BorderWidth--sm, 1px) dashed var(--pf-global--BorderColor--100, #d2d2d2);
121+
margin-block-end: var(--pf-global--spacer--xs, 0.25rem);
122+
padding-block: var(--pf-global--spacer--2xl, 3rem) var(--pf-global--spacer--4xl, 6rem) ;
123+
padding-inline: var(--pf-global--spacer--md, 1rem);
124+
}
125+
126+
pf-jump-links {
127+
position: sticky;
128+
inset: 0;
129+
height: 100dvh;
130+
}
131+
</style>

0 commit comments

Comments
 (0)