Skip to content

Commit

Permalink
Replace hacky window.setTimeout(50) callback with scrollend event…
Browse files Browse the repository at this point in the history
… to `keepActiveTocItemInView` (#53)

* bump pre-commit and pkg deps

* use scrollend event over hacky window.setTimeout(50) callback to keepActiveTocItemInView

* readme document required scrollend event browser support for keepActiveTocItemInView=true

* fix build by pnpm adding @sveltejs/vite-plugin-svelte and removing importsNotUsedAsValues from tsconfig.json
  • Loading branch information
janosh committed Jan 22, 2024
1 parent 532acf0 commit 2f49c33
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 46 deletions.
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
rev: v4.0.0-alpha.8
hooks:
- id: prettier
args: [--write] # edit files in-place
Expand All @@ -32,10 +32,10 @@ repos:
hooks:
- id: codespell
stages: [commit, commit-msg]
args: [--ignore-words-list, falsy]
args: [--ignore-words-list, falsy, --check-filenames]

- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.51.0
rev: v9.0.0-alpha.1
hooks:
- id: eslint
types: [file]
Expand Down
43 changes: 22 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,34 @@
"update-coverage": "vitest tests/unit --run --coverage && npx istanbul-badges-readme"
},
"dependencies": {
"svelte": "^4.2.1"
"svelte": "^4.2.9"
},
"devDependencies": {
"@playwright/test": "1.38.1",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.25.2",
"@sveltejs/package": "^2.2.2",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5",
"@vitest/coverage-v8": "^0.34.6",
"eslint": "^8.51.0",
"eslint-plugin-svelte": "^2.34.0",
"hastscript": "^8.0.0",
"jsdom": "^22.1.0",
"@playwright/test": "1.41.1",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.4.1",
"@sveltejs/package": "^2.2.6",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitest/coverage-v8": "^1.2.1",
"eslint": "^8.56.0",
"eslint-plugin-svelte": "^2.35.1",
"hastscript": "^9.0.0",
"jsdom": "^24.0.0",
"mdsvex": "^0.11.0",
"mdsvexamples": "^0.4.1",
"prettier": "^3.0.3",
"prettier-plugin-svelte": "^3.0.3",
"rehype-autolink-headings": "^7.0.0",
"prettier": "^3.2.4",
"prettier-plugin-svelte": "^3.1.2",
"rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"svelte-check": "^3.5.2",
"svelte-preprocess": "^5.0.4",
"svelte-check": "^3.6.3",
"svelte-preprocess": "^5.1.3",
"svelte-zoo": "^0.4.9",
"svelte2tsx": "^0.6.23",
"typescript": "5.2.2",
"vite": "^4.4.11",
"vitest": "^0.34.6"
"svelte2tsx": "^0.7.0",
"typescript": "5.3.3",
"vite": "^5.0.12",
"vitest": "^1.2.1"
},
"keywords": [
"svelte",
Expand Down
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ Full list of props and bindable variables for this component (all of them option
keepActiveTocItemInView: boolean = true
```

Whether to scroll the ToC along with the page.
Whether to keep the active ToC item in view when scrolling the page. Only applies to long ToCs that are too high to fit on screen. If true, the ToC container will scroll itself to keep the active item in view and centered (if possible).
Requires [`scrollend` event](https://developer.mozilla.org/en-US/docs/Web/API/Document/scrollend_event) browser support ([71% as of 2024-01-22](https://caniuse.com/mdn-api_element_scrollend_event)), with Safari the only major browser lacking support.

1. ```ts
minItems: number = 0
Expand Down
30 changes: 13 additions & 17 deletions src/lib/Toc.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
export let headingSelector: string = `:is(h2, h3, h4):not(.toc-exclude)`
export let hide: boolean = false
export let autoHide: boolean = true
export let keepActiveTocItemInView: boolean = true
export let keepActiveTocItemInView: boolean = true // requires scrollend event browser support
export let minItems: number = 0
export let open: boolean = false
export let openButtonLabel: string = `Open table of contents`
Expand All @@ -35,7 +35,6 @@
let aside: HTMLElement
let nav: HTMLElement
let scroll_id: number
$: levels = headings.map(getHeadingLevels)
$: minLevel = Math.min(...levels)
$: desktop = window_width > breakpoint
Expand All @@ -54,7 +53,7 @@
console.warn(
`svelte-toc found no headings for headingSelector='${headingSelector}'. ${
autoHide ? `Hiding` : `Showing empty`
} table of contents.`
} table of contents.`,
)
}
if (autoHide) hide = true
Expand Down Expand Up @@ -87,20 +86,6 @@
if (top < activeHeadingScrollOffset || idx === 0) {
activeHeading = headings[idx]
activeTocLi = tocItems[idx]
// this annoying hackery to wait for scroll end is necessary because scrollend event only has 2%
// browser support https://stackoverflow.com/a/57867348 and Chrome doesn't support multiple
// simultaneous scrolls, smooth or otherwise (https://stackoverflow.com/a/63563437)
clearTimeout(scroll_id)
scroll_id = window.setTimeout(() => {
if (keepActiveTocItemInView && activeTocLi) {
// get the currently active ToC list item
// scroll the active ToC item into the middle of the ToC container
nav.scrollTo?.({
top: activeTocLi?.offsetTop - nav.offsetHeight / 2,
behavior: `smooth`,
})
}
}, 50)
return // exit while loop if updated active heading
}
}
Expand All @@ -125,6 +110,17 @@
bind:innerWidth={window_width}
on:scroll={set_active_heading}
on:click={close}
on:scrollend={() => {
// wait for scroll end since Chrome doesn't support multiple simultaneous scrolls,
// smooth or otherwise (https://stackoverflow.com/a/63563437)
if (keepActiveTocItemInView && activeTocLi) {
// scroll the active ToC item into the middle of the ToC container
nav.scrollTo?.({
top: activeTocLi?.offsetTop - nav.offsetHeight / 2,
behavior: `smooth`,
})
}
}}
/>

<aside
Expand Down
4 changes: 0 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
"target": "esnext",
"moduleResolution": "bundler",

// Svelte Preprocess cannot figure out whether you have a value or a type, so tell TypeScript
// to enforce using `import type` instead of `import` for Types.
"importsNotUsedAsValues": "error",

// To have warnings/errors of the Svelte compiler at the correct position,
// enable source maps by default.
"sourceMap": true,
Expand Down

0 comments on commit 2f49c33

Please sign in to comment.