Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/every-maps-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@patternfly/pfe-core": patch
---
`ScrollSpyController`: improve responsiveness of scroll spy

54 changes: 32 additions & 22 deletions core/pfe-core/controllers/scroll-spy-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ export class ScrollSpyController implements ReactiveController {
static {
if (!isServer) {
addEventListener('scroll', () => {
if (Math.round(window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
this.#instances.forEach(ssc => {
ssc.#setActive(ssc.#linkChildren.at(-1));
});
}
this.#instances.forEach(ssc => {
ssc.#reconcile();
});
}, { passive: true });
addEventListener('scrollend', () => {
this.#instances.forEach(ssc => {
ssc.#reconcile();
});
}, { passive: true });
addEventListener('hashchange', () => {
this.#instances.forEach(ssc => {
Expand Down Expand Up @@ -72,9 +75,10 @@ export class ScrollSpyController implements ReactiveController {

#threshold: number | number[];

#intersectingTargets = new Set<Element>();
#intersectionEntries = new Set<IntersectionObserverEntry>();

#linkTargetMap = new Map<Element, Element | null>();
#targetLinkMap = new Map<Element, Element | null>();

#getRootNode: () => Node | null;

Expand Down Expand Up @@ -148,6 +152,24 @@ export class ScrollSpyController implements ReactiveController {

#initializing = true;

#reconcile() {
const { scrollY, innerHeight } = window;
let link: Element | null | undefined = null;
if (scrollY === 0) {
link = this.#linkChildren.at(0);
} else if (Math.round(innerHeight + scrollY) >= document.body.scrollHeight) {
link = this.#linkChildren.at(-1);
} else {
const [entry] = [...this.#intersectionEntries].sort((a, b) => {
return b.boundingClientRect.y - a.boundingClientRect.y;
});
link = this.#targetLinkMap.get(entry?.target);
}
if (link) {
this.#setActive(link);
}
}

async #initIo() {
const rootNode = this.#getRootNode();
if (rootNode instanceof Document || rootNode instanceof ShadowRoot) {
Expand All @@ -160,6 +182,7 @@ export class ScrollSpyController implements ReactiveController {
if (target) {
this.#io?.observe(target);
this.#linkTargetMap.set(link, target);
this.#targetLinkMap.set(target, link);
}
}
}
Expand Down Expand Up @@ -209,32 +232,19 @@ export class ScrollSpyController implements ReactiveController {
this.#markPassed(link, boundingClientRect.top < intersectionRect.top);
}
}
const link = [...this.#passedLinks];
const last = link.at(-1);
this.#setActive(last ?? this.#linkChildren.at(0));
}
this.#intersected = true;
this.#intersectingTargets.clear();
this.#intersectionEntries.clear();
for (const entry of entries) {
if (entry.isIntersecting) {
this.#intersectingTargets.add(entry.target);
this.#intersectionEntries.add(entry);
}
}
if (this.#initializing) {
const ints = entries?.filter(x => x.isIntersecting) ?? [];
if (this.#intersectingTargets.size > 0) {
const [{ target = null } = {}] = ints;
const { id } = target ?? {};
if (id) {
const link = this.#linkChildren.find(link => this.#getHash(link) === `#${id}`);
if (link) {
this.#setActive(link);
}
}
}
this.#initializing = false;
}
this.#onIntersection?.();
this.#reconcile();
}

/**
Expand Down
131 changes: 131 additions & 0 deletions elements/pf-jump-links/demo/non-contiguous.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<article>
<div class="content">
<section id="chapter-1">
<h1>Chapter 1</h1>
</section>
<section id="chapter-2">
<h2>
<a href="#chapter-2">Chapter 2</a>
</h2>
</section>
<section id="chapter-3">
<h2>
<a href="#chapter-3">Chapter 3</a>
</h2>
<section id="sub-chapter-1">
<h3>
<a href="#sub-chapter-1">Sub-chapter 3.1</a>
</h3>
</section>
<section id="sub-chapter-2">
<h3>
<a href="#sub-chapter-2">Sub-chapter 3.2</a>
</h3>
</section>
<section id="sub-chapter-3">
<h3>
<a href="#sub-chapter-3">Sub-chapter 3.3</a>
</h3>
</section>
<section id="sub-chapter-4">
<h3>
<a href="#sub-chapter-4">Sub-chapter 3.4</a>
</h3>
</section>
<section id="sub-chapter-5">
<h3>
<a href="#sub-chapter-5">Sub-chapter 3.5</a>
</h3>
</section>
<section id="sub-chapter-6">
<h3>
<a href="#sub-chapter-6">Sub-chapter 3.6</a>
</h3>
</section>
<section id="sub-chapter-7">
<h3>
<a href="#sub-chapter-7">Sub-chapter 3.7</a>
</h3>
</section>
<section id="sub-chapter-8">
<h3>
<a href="#sub-chapter-8">Sub-chapter 3.8</a>
</h3>
</section>
<section id="sub-chapter-9">
<h3>
<a href="#sub-chapter-9">sub-chapter 3.9</a>
</h3>
</section>
<section id="sub-chapter-10">
<h3>
<a href="#sub-chapter-10">Sub-chapter 3.10</a>
</h3>
</section>
</section>
<section id="chapter-4">
<h2>
<a href="#chapter-4">Chapter 4</a>
</h2>
</section>
<section id="chapter-5">
<h2>
<a href="#chapter-5">Chapter 5</a>
</h2>
<section id="sub-chapter-11">
<h3>
<a href="#sub-chapter-11">Sub-chapter 5.1</a>
</h3>
</section>
<section id="sub-chapter-12">
<h3>
<a href="#sub-chapter-12">Sub-chapter 5.2</a>
</h3>
</section>
</section>
</div>
<pf-jump-links vertical>
<pf-jump-links-item href="#sub-chapter-1">Sub-chapter 3.1</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-2">Sub-chapter 3.2</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-3">Sub-chapter 3.3</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-4">Sub-chapter 3.4</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-5">Sub-chapter 3.5</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-6">Sub-chapter 3.6</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-7">Sub-chapter 3.7</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-8">Sub-chapter 3.8</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-9">Sub-chapter 3.9</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-10">Sub-chapter 3.10</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-11">Sub-chapter 5.1</pf-jump-links-item>
<pf-jump-links-item href="#sub-chapter-12">Sub-chapter 5.2</pf-jump-links-item>
</pf-jump-links>
</article>


<script type="module">
import '@patternfly/elements/pf-jump-links/pf-jump-links.js';
</script>

<style>
article {
display: grid;
grid-template-columns: 1fr 0.25fr;
gap: var(--pf-global--spacer--md, 1rem);
}

.content {
min-height: 100dvh;
}

section {
border: var(--pf-global--BorderWidth--sm, 1px) dashed var(--pf-global--BorderColor--100, #d2d2d2);
margin-block-end: var(--pf-global--spacer--xs, 0.25rem);
padding-block: var(--pf-global--spacer--2xl, 3rem) var(--pf-global--spacer--4xl, 6rem) ;
padding-inline: var(--pf-global--spacer--md, 1rem);
}

pf-jump-links {
position: sticky;
inset: 0;
height: 100dvh;
}
</style>
Loading