]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
ScrollSpy: make the threshold option configurable (#36750)
authorGeoSot <geo.sotis@gmail.com>
Thu, 28 Jul 2022 08:58:28 +0000 (11:58 +0300)
committerGitHub <noreply@github.com>
Thu, 28 Jul 2022 08:58:28 +0000 (11:58 +0300)
* feat(ScrollSpy): make the threshold option configurable

js/src/scrollspy.js
js/tests/unit/scrollspy.spec.js
site/content/docs/5.2/components/scrollspy.md

index 102a2e1010f9e3ea49b0996c594b38854d995a10..f4d6671013b074a73dcfe3f988f7b75a6a4f9811 100644 (file)
@@ -40,14 +40,16 @@ const Default = {
   offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons
   rootMargin: '0px 0px -25%',
   smoothScroll: false,
-  target: null
+  target: null,
+  threshold: [0.1, 0.5, 1]
 }
 
 const DefaultType = {
   offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons
   rootMargin: 'string',
   smoothScroll: 'boolean',
-  target: 'element'
+  target: 'element',
+  threshold: 'array'
 }
 
 /**
@@ -110,6 +112,13 @@ class ScrollSpy extends BaseComponent {
     // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case
     config.target = getElement(config.target) || document.body
 
+    // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only
+    config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin
+
+    if (typeof config.threshold === 'string') {
+      config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))
+    }
+
     return config
   }
 
@@ -141,8 +150,8 @@ class ScrollSpy extends BaseComponent {
   _getNewObserver() {
     const options = {
       root: this._rootElement,
-      threshold: [0.1, 0.5, 1],
-      rootMargin: this._getRootMargin()
+      threshold: this._config.threshold,
+      rootMargin: this._config.rootMargin
     }
 
     return new IntersectionObserver(entries => this._observerCallback(entries), options)
@@ -187,11 +196,6 @@ class ScrollSpy extends BaseComponent {
     }
   }
 
-  // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only
-  _getRootMargin() {
-    return this._config.offset ? `${this._config.offset}px 0px -30%` : this._config.rootMargin
-  }
-
   _initializeTargetsAndObservables() {
     this._targetLinks = new Map()
     this._observableSections = new Map()
index 2bdeb5830cfff231ff84f68743bd1553e35ca894..c7951e6ff12de8210735c35456316aa981ac11c5 100644 (file)
@@ -126,6 +126,50 @@ describe('ScrollSpy', () => {
       expect(scrollSpy._rootElement).toBeNull()
     })
 
+    it('should respect threshold option', () => {
+      fixtureEl.innerHTML = [
+        '<ul id="navigation" class="navbar">',
+        '   <a class="nav-link active" id="one-link" href="#">One</a>' +
+        '</ul>',
+        '<div id="content">',
+        '  <div id="one-link">test</div>',
+        '</div>'
+      ].join('')
+
+      const scrollSpy = new ScrollSpy('#content', {
+        target: '#navigation',
+        threshold: [1]
+      })
+
+      expect(scrollSpy._observer.thresholds).toEqual([1])
+    })
+
+    it('should respect threshold option markup', () => {
+      fixtureEl.innerHTML = [
+        '<ul id="navigation" class="navbar">',
+        '   <a class="nav-link active" id="one-link" href="#">One</a>' +
+        '</ul>',
+        '<div id="content" data-bs-threshold="0,0.2,1">',
+        '  <div id="one-link">test</div>',
+        '</div>'
+      ].join('')
+
+      const scrollSpy = new ScrollSpy('#content', {
+        target: '#navigation'
+      })
+
+      // See https://stackoverflow.com/a/45592926
+      const expectToBeCloseToArray = (actual, expected) => {
+        expect(actual.length).toBe(expected.length)
+        for (const x of actual) {
+          const i = actual.indexOf(x)
+          expect(x).withContext(`[${i}]`).toBeCloseTo(expected[i])
+        }
+      }
+
+      expectToBeCloseToArray(scrollSpy._observer.thresholds, [0, 0.2, 1])
+    })
+
     it('should not take count to not visible sections', () => {
       fixtureEl.innerHTML = [
         '<nav id="navigation" class="navbar">',
index 5e329dc85a2e00618e2eb30ac217d6359ac2a6d3..b2461f0dae6e36dbd7062503ffb5602f48f55656 100644 (file)
@@ -380,6 +380,8 @@ const scrollSpy = new bootstrap.ScrollSpy(document.body, {
 | `rootMargin` | string | `0px 0px -40%` | Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) valid units, when calculating scroll position. |
 | `smoothScroll` | boolean | `false` | Enables smooth scrolling when a user clicks on a link that refers to ScrollSpy observables. |
 | `target` | string \| jQuery object \| DOM element |  | Specifies element to apply Scrollspy plugin. |
+| `threshold` | array | `[0.1, 0.5, 1]` | `IntersectionObserver` [threshold](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#threshold) valid input, when calculating scroll position.|
+
 {{< /bs-table >}}
 
 {{< callout warning >}}