@zachleat/table-of-contents 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/table-of-contents.js +21 -4
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ TableOfContents.define("my-toc");
|
|
|
39
39
|
## Attributes
|
|
40
40
|
|
|
41
41
|
- `selector`: headings to include (default `main :is(h2, h3, h4, h5, h6)`). Headings without an `id` are listed without a link.
|
|
42
|
+
- `best-match`: when multiple sections are in view, highlight only the one taking up the most vertical space in the viewport.
|
|
42
43
|
|
|
43
44
|
## Styling
|
|
44
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zachleat/table-of-contents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A zero-dependency web component that progressively enhances (or optionally client renders) a table of contents from the headings on the page and highlights visible sections.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "table-of-contents.js",
|
package/table-of-contents.js
CHANGED
|
@@ -172,19 +172,36 @@ export class TableOfContents extends HTMLElement {
|
|
|
172
172
|
|
|
173
173
|
let headingSet = new Set(headings);
|
|
174
174
|
let targets = new Map();
|
|
175
|
-
let visible = new
|
|
175
|
+
let visible = new Map();
|
|
176
|
+
let bestMatch = this.hasAttribute("best-match");
|
|
176
177
|
|
|
177
178
|
this.observer = new IntersectionObserver(entries => {
|
|
178
179
|
for(let entry of entries) {
|
|
179
|
-
|
|
180
|
+
if(entry.isIntersecting) {
|
|
181
|
+
visible.set(entry.target, entry.intersectionRect.height);
|
|
182
|
+
} else {
|
|
183
|
+
visible.delete(entry.target);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let heights = new Map();
|
|
188
|
+
for(let [el, height] of visible) {
|
|
189
|
+
let id = targets.get(el);
|
|
190
|
+
heights.set(id, (heights.get(id) || 0) + height);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let activeIds = new Set(heights.keys());
|
|
194
|
+
if(bestMatch && activeIds.size > 1) {
|
|
195
|
+
// Ties go to the earlier heading.
|
|
196
|
+
let best = headings.map(h => h.id).filter(id => heights.has(id)).reduce((a, b) => heights.get(b) > heights.get(a) ? b : a);
|
|
197
|
+
activeIds = new Set([best]);
|
|
180
198
|
}
|
|
181
199
|
|
|
182
|
-
let activeIds = new Set(Array.from(visible, el => targets.get(el)));
|
|
183
200
|
for(let link of this.list.querySelectorAll("a[href^='#']")) {
|
|
184
201
|
let id = link.getAttribute("href").slice(1);
|
|
185
202
|
link.classList.toggle(TableOfContents.defaults.activeClass, activeIds.has(id));
|
|
186
203
|
}
|
|
187
|
-
});
|
|
204
|
+
}, bestMatch ? { threshold: Array.from({ length: 21 }, (_, i) => i / 20) } : undefined);
|
|
188
205
|
|
|
189
206
|
for(let heading of headings) {
|
|
190
207
|
if(!heading.id) {
|