Skip to content

Commit b226aeb

Browse files
committed
feat(pat scroll-box): Allow detection of scroll-stop.
Add ``scrolling-up`` and ``scrolling-down`` classes which will be removed after the user has stopped scrolling. This allows for detection of a scrolling situation vs non-scrolling situation.
1 parent 7e7fd23 commit b226aeb

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

src/pat/scroll-box/scroll-box.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import Base from "../../core/base";
2+
import Parser from "../../core/parser";
23
import events from "../../core/events";
34

5+
export const parser = new Parser("scroll-box");
6+
parser.addArgument("timeout-stop", 600); // Timeout to detect when stopping scrolling.
7+
48
export default Base.extend({
59
name: "scroll-box",
610
trigger: ".pat-scroll-box",
@@ -11,6 +15,7 @@ export default Base.extend({
1115

1216
init() {
1317
const el = this.el;
18+
this.options = parser.parse(el);
1419
this.scroll_listener = el === document.body ? window : el;
1520

1621
// If scolling is not possible, exit.
@@ -31,6 +36,14 @@ export default Base.extend({
3136
if (!ticking) {
3237
window.requestAnimationFrame(() => {
3338
this.set_scroll_classes();
39+
40+
// Detect case when user stops scrolling.
41+
window.clearTimeout(this.timeout_id__scroll_stop);
42+
this.timeout_id__scroll_stop = window.setTimeout(() => {
43+
// When user stopped scrolling, set/clear scroll classes.
44+
this.clear_scrolling_classes();
45+
}, this.options.timeoutStop);
46+
3447
ticking = false;
3548
});
3649
ticking = true;
@@ -47,13 +60,17 @@ export default Base.extend({
4760
const el = this.el;
4861
el.classList.remove("scroll-up");
4962
el.classList.remove("scroll-down");
63+
el.classList.remove("scrolling-up");
64+
el.classList.remove("scrolling-down");
5065
el.classList.remove("scroll-position-top");
5166
el.classList.remove("scroll-position-bottom");
5267

5368
if (scroll_pos < this.last_known_scroll_position) {
5469
el.classList.add("scroll-up");
70+
el.classList.add("scrolling-up");
5571
} else if (this.last_known_scroll_position < scroll_pos) {
5672
el.classList.add("scroll-down");
73+
el.classList.add("scrolling-down");
5774
}
5875

5976
if (scroll_pos === 0) {
@@ -72,6 +89,10 @@ export default Base.extend({
7289
this.last_known_scroll_position = scroll_pos;
7390
},
7491

92+
clear_scrolling_classes() {
93+
this.el.classList.remove("scrolling-up", "scrolling-down");
94+
},
95+
7596
get_scroll_y() {
7697
if (this.scroll_listener === window) {
7798
// scrolling the window

src/pat/scroll-box/scroll-box.test.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
import pattern from "./scroll-box";
1+
import pattern, { parser } from "./scroll-box";
22
import utils from "../../core/utils";
33
import events from "../../core/events";
44

55
describe("pat-scroll-box", function () {
6+
let orig_timeout;
7+
let custom_timeout = 10; // reduced timeout for faster testing
8+
beforeEach(function () {
9+
orig_timeout = parser.parameters["timeout-stop"].value;
10+
parser.parameters["timeout-stop"].value = custom_timeout;
11+
});
12+
613
afterEach(function () {
714
jest.restoreAllMocks();
815
document.body.innerHTML = "";
16+
parser.parameters["timeout-stop"].value = orig_timeout;
917
});
1018

11-
it("1 - Basic functionality", async function () {
19+
it("Basic functionality", async function () {
1220
document.body.innerHTML = `
1321
<div id="el1" style="overflow: scroll"></div>
1422
`;
@@ -28,6 +36,8 @@ describe("pat-scroll-box", function () {
2836
expect(el.classList).not.toContain("scroll-position-bottom");
2937
expect(el.classList).not.toContain("scroll-up");
3038
expect(el.classList).not.toContain("scroll-down");
39+
expect(el.classList).not.toContain("scrolling-up");
40+
expect(el.classList).not.toContain("scrolling-down");
3141

3242
el.scrollTop = 100;
3343
el.dispatchEvent(events.scroll_event());
@@ -36,6 +46,8 @@ describe("pat-scroll-box", function () {
3646
expect(el.classList).not.toContain("scroll-position-bottom");
3747
expect(el.classList).not.toContain("scroll-up");
3848
expect(el.classList).toContain("scroll-down");
49+
expect(el.classList).not.toContain("scrolling-up");
50+
expect(el.classList).toContain("scrolling-down");
3951

4052
el.scrollTop = 50;
4153
el.dispatchEvent(events.scroll_event());
@@ -44,6 +56,8 @@ describe("pat-scroll-box", function () {
4456
expect(el.classList).not.toContain("scroll-position-bottom");
4557
expect(el.classList).toContain("scroll-up");
4658
expect(el.classList).not.toContain("scroll-down");
59+
expect(el.classList).toContain("scrolling-up");
60+
expect(el.classList).not.toContain("scrolling-down");
4761

4862
el.scrollTop = 200;
4963
el.dispatchEvent(events.scroll_event());
@@ -52,6 +66,8 @@ describe("pat-scroll-box", function () {
5266
expect(el.classList).toContain("scroll-position-bottom");
5367
expect(el.classList).not.toContain("scroll-up");
5468
expect(el.classList).toContain("scroll-down");
69+
expect(el.classList).not.toContain("scrolling-up");
70+
expect(el.classList).toContain("scrolling-down");
5571

5672
el.scrollTop = 0;
5773
el.dispatchEvent(events.scroll_event());
@@ -60,5 +76,33 @@ describe("pat-scroll-box", function () {
6076
expect(el.classList).not.toContain("scroll-position-bottom");
6177
expect(el.classList).toContain("scroll-up");
6278
expect(el.classList).not.toContain("scroll-down");
79+
expect(el.classList).toContain("scrolling-up");
80+
expect(el.classList).not.toContain("scrolling-down");
81+
82+
// Test for clearing the scrolling classes after a scroll stop
83+
// Still there...
84+
await utils.timeout(custom_timeout / 2);
85+
expect(el.classList).toContain("scrolling-up");
86+
// Now gone
87+
await utils.timeout(custom_timeout / 2 + 1);
88+
expect(el.classList).not.toContain("scrolling-up");
89+
90+
el.scrollTop = 100;
91+
el.dispatchEvent(events.scroll_event());
92+
await utils.animation_frame();
93+
expect(el.classList).not.toContain("scroll-position-top");
94+
expect(el.classList).not.toContain("scroll-position-bottom");
95+
expect(el.classList).not.toContain("scroll-up");
96+
expect(el.classList).toContain("scroll-down");
97+
expect(el.classList).not.toContain("scrolling-up");
98+
expect(el.classList).toContain("scrolling-down");
99+
100+
// Test for clearing the scrolling classes after a scroll stop
101+
// Still there...
102+
await utils.timeout(custom_timeout / 2);
103+
expect(el.classList).toContain("scrolling-down");
104+
// Now gone
105+
await utils.timeout(custom_timeout / 2 + 1);
106+
expect(el.classList).not.toContain("scrolling-down");
63107
});
64108
});

0 commit comments

Comments
 (0)