Handle scrollable elements for highlighting
Browse files- index.html +87 -2
- src/config.ts +2 -0
- src/highlight.ts +3 -25
- src/math.ts +0 -11
- src/stage.ts +1 -1
- src/utils.ts +52 -0
index.html
CHANGED
@@ -94,6 +94,15 @@
|
|
94 |
padding: 10px;
|
95 |
line-height: 1.75;
|
96 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
</style>
|
98 |
</head>
|
99 |
<body>
|
@@ -110,6 +119,8 @@
|
|
110 |
<button id="disallow-close">Disallow Close</button>
|
111 |
<button id="dark-highlight-btn">Super Dark Highlight</button>
|
112 |
<button id="dim-highlight-btn">Super Dim Highlight</button>
|
|
|
|
|
113 |
<button id="tour-btn">Start Tour</button>
|
114 |
<button id="destroy-btn">Destroy</button>
|
115 |
</div>
|
@@ -131,14 +142,14 @@
|
|
131 |
off the Lights" widgets that you might have seen on video players
|
132 |
online, etc.
|
133 |
</p>
|
134 |
-
<p>
|
135 |
Driver.js is written in Vanilla JS, has zero dependencies and is highly
|
136 |
customizable. It has several options allowing you to manipulate how it
|
137 |
behaves and also provides you the hooks to manipulate the elements as
|
138 |
they are highlighted, about to be highlighted, or deselected.
|
139 |
</p>
|
140 |
|
141 |
-
<h2>Installation</h2>
|
142 |
<p>You can install it using yarn or npm, whatever you prefer.</p>
|
143 |
|
144 |
<pre>
|
@@ -175,6 +186,62 @@ npm install driver.js</pre
|
|
175 |
libero, minus molestias necessitatibus nesciunt non omnis, quasi
|
176 |
recusandae tempore voluptates!
|
177 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
<p>
|
179 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi
|
180 |
blanditiis consectetur ea eligendi id in inventore ipsa iure laudantium
|
@@ -249,6 +316,24 @@ npm install driver.js</pre
|
|
249 |
window.setTimeout(() => {
|
250 |
driverObj.highlight({ element: ".buttons" });
|
251 |
}, 1000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
});
|
253 |
|
254 |
document
|
|
|
94 |
padding: 10px;
|
95 |
line-height: 1.75;
|
96 |
}
|
97 |
+
|
98 |
+
#scrollable-area {
|
99 |
+
height: 300px;
|
100 |
+
overflow: auto;
|
101 |
+
border: 1px solid #ccc;
|
102 |
+
padding: 10px;
|
103 |
+
border-radius: 5px;
|
104 |
+
margin: 50px 0;
|
105 |
+
}
|
106 |
</style>
|
107 |
</head>
|
108 |
<body>
|
|
|
119 |
<button id="disallow-close">Disallow Close</button>
|
120 |
<button id="dark-highlight-btn">Super Dark Highlight</button>
|
121 |
<button id="dim-highlight-btn">Super Dim Highlight</button>
|
122 |
+
<button id="scrollable-area-btn">Scrollable Area</button>
|
123 |
+
<button id="inner-scroll-area-btn">Inner Scroll Area</button>
|
124 |
<button id="tour-btn">Start Tour</button>
|
125 |
<button id="destroy-btn">Destroy</button>
|
126 |
</div>
|
|
|
142 |
off the Lights" widgets that you might have seen on video players
|
143 |
online, etc.
|
144 |
</p>
|
145 |
+
<p class="second-para">
|
146 |
Driver.js is written in Vanilla JS, has zero dependencies and is highly
|
147 |
customizable. It has several options allowing you to manipulate how it
|
148 |
behaves and also provides you the hooks to manipulate the elements as
|
149 |
they are highlighted, about to be highlighted, or deselected.
|
150 |
</p>
|
151 |
|
152 |
+
<h2 id="installation-head">Installation</h2>
|
153 |
<p>You can install it using yarn or npm, whatever you prefer.</p>
|
154 |
|
155 |
<pre>
|
|
|
186 |
libero, minus molestias necessitatibus nesciunt non omnis, quasi
|
187 |
recusandae tempore voluptates!
|
188 |
</p>
|
189 |
+
<div id="scrollable-area">
|
190 |
+
<p>
|
191 |
+
First -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
192 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
193 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
194 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
195 |
+
</p>
|
196 |
+
<p>
|
197 |
+
Second -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
198 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
199 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
200 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
201 |
+
</p>
|
202 |
+
<p id="third-scroll-paragraph">
|
203 |
+
Third -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
204 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
205 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
206 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
207 |
+
</p>
|
208 |
+
<p>
|
209 |
+
Fourth -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
210 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
211 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
212 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
213 |
+
</p>
|
214 |
+
<p>
|
215 |
+
Fifth -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
216 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
217 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
218 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
219 |
+
</p>
|
220 |
+
<p>
|
221 |
+
Sixth -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
222 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
223 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
224 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
225 |
+
</p>
|
226 |
+
<p>
|
227 |
+
Seventh -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
228 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
229 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
230 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
231 |
+
</p>
|
232 |
+
<p>
|
233 |
+
Eighth -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
234 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
235 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
236 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
237 |
+
</p>
|
238 |
+
<p>
|
239 |
+
Ninth -> Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
240 |
+
Consequuntur dicta ipsum labore quod tempora ullam? Alias consequatur
|
241 |
+
doloremque laborum maxime necessitatibus nostrum odio, officiis
|
242 |
+
quibusdam veniam! Doloribus eos id quaerat.
|
243 |
+
</p>
|
244 |
+
</div>
|
245 |
<p>
|
246 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi
|
247 |
blanditiis consectetur ea eligendi id in inventore ipsa iure laudantium
|
|
|
316 |
window.setTimeout(() => {
|
317 |
driverObj.highlight({ element: ".buttons" });
|
318 |
}, 1000);
|
319 |
+
|
320 |
+
window.setTimeout(() => {
|
321 |
+
driverObj.highlight({ element: "#installation-head" });
|
322 |
+
}, 1500);
|
323 |
+
});
|
324 |
+
|
325 |
+
document
|
326 |
+
.getElementById("scrollable-area-btn")
|
327 |
+
.addEventListener("click", () => {
|
328 |
+
const driverObj = driver({ animate: true });
|
329 |
+
driverObj.highlight({ element: "#scrollable-area" });
|
330 |
+
});
|
331 |
+
|
332 |
+
document
|
333 |
+
.getElementById("inner-scroll-area-btn")
|
334 |
+
.addEventListener("click", () => {
|
335 |
+
const driverObj = driver({ animate: true });
|
336 |
+
driverObj.highlight({ element: "#third-scroll-paragraph" });
|
337 |
});
|
338 |
|
339 |
document
|
src/config.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
export type Config = {
|
2 |
animate?: boolean;
|
|
|
3 |
allowClose?: boolean;
|
4 |
opacity?: number;
|
5 |
};
|
@@ -11,6 +12,7 @@ export function configure(config: Config = {}) {
|
|
11 |
animate: true,
|
12 |
allowClose: true,
|
13 |
opacity: 0.7,
|
|
|
14 |
...config,
|
15 |
};
|
16 |
}
|
|
|
1 |
export type Config = {
|
2 |
animate?: boolean;
|
3 |
+
smoothScroll?: boolean;
|
4 |
allowClose?: boolean;
|
5 |
opacity?: number;
|
6 |
};
|
|
|
12 |
animate: true,
|
13 |
allowClose: true,
|
14 |
opacity: 0.7,
|
15 |
+
smoothScroll: true,
|
16 |
...config,
|
17 |
};
|
18 |
}
|
src/highlight.ts
CHANGED
@@ -2,6 +2,7 @@ import { DriveStep } from "./driver";
|
|
2 |
import { refreshStage, trackActiveElement, transitionStage } from "./stage";
|
3 |
import { getConfig } from "./config";
|
4 |
import { refreshPopover, renderPopover } from "./popover";
|
|
|
5 |
|
6 |
let previousHighlight: Element | undefined;
|
7 |
let activeHighlight: Element | undefined;
|
@@ -48,7 +49,6 @@ function transferHighlight(from: Element, to: Element) {
|
|
48 |
if (getConfig("animate") && elapsed < duration) {
|
49 |
transitionStage(elapsed, duration, from, to);
|
50 |
} else {
|
51 |
-
bringInView(to);
|
52 |
trackActiveElement(to);
|
53 |
renderPopover(to);
|
54 |
|
@@ -63,34 +63,12 @@ function transferHighlight(from: Element, to: Element) {
|
|
63 |
currentTransitionCallback = animate;
|
64 |
window.requestAnimationFrame(animate);
|
65 |
|
|
|
|
|
66 |
from.classList.remove("driver-active-element");
|
67 |
to.classList.add("driver-active-element");
|
68 |
}
|
69 |
|
70 |
-
function bringInView(element: Element) {
|
71 |
-
if (!element || isElementInView(element)) {
|
72 |
-
return;
|
73 |
-
}
|
74 |
-
|
75 |
-
element.scrollIntoView({
|
76 |
-
behavior: "smooth",
|
77 |
-
inline: "center",
|
78 |
-
block: "center",
|
79 |
-
});
|
80 |
-
}
|
81 |
-
|
82 |
-
function isElementInView(element: Element) {
|
83 |
-
const rect = element.getBoundingClientRect();
|
84 |
-
|
85 |
-
return (
|
86 |
-
rect.top >= 0 &&
|
87 |
-
rect.left >= 0 &&
|
88 |
-
rect.bottom <=
|
89 |
-
(window.innerHeight || document.documentElement.clientHeight) &&
|
90 |
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
91 |
-
);
|
92 |
-
}
|
93 |
-
|
94 |
export function destroyHighlight() {
|
95 |
activeHighlight = undefined;
|
96 |
currentTransitionCallback = undefined;
|
|
|
2 |
import { refreshStage, trackActiveElement, transitionStage } from "./stage";
|
3 |
import { getConfig } from "./config";
|
4 |
import { refreshPopover, renderPopover } from "./popover";
|
5 |
+
import { bringInView } from "./utils";
|
6 |
|
7 |
let previousHighlight: Element | undefined;
|
8 |
let activeHighlight: Element | undefined;
|
|
|
49 |
if (getConfig("animate") && elapsed < duration) {
|
50 |
transitionStage(elapsed, duration, from, to);
|
51 |
} else {
|
|
|
52 |
trackActiveElement(to);
|
53 |
renderPopover(to);
|
54 |
|
|
|
63 |
currentTransitionCallback = animate;
|
64 |
window.requestAnimationFrame(animate);
|
65 |
|
66 |
+
bringInView(to);
|
67 |
+
|
68 |
from.classList.remove("driver-active-element");
|
69 |
to.classList.add("driver-active-element");
|
70 |
}
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
export function destroyHighlight() {
|
73 |
activeHighlight = undefined;
|
74 |
currentTransitionCallback = undefined;
|
src/math.ts
DELETED
@@ -1,11 +0,0 @@
|
|
1 |
-
export function easeInOutQuad(
|
2 |
-
elapsed: number,
|
3 |
-
initialValue: number,
|
4 |
-
amountOfChange: number,
|
5 |
-
duration: number
|
6 |
-
): number {
|
7 |
-
if ((elapsed /= duration / 2) < 1) {
|
8 |
-
return (amountOfChange / 2) * elapsed * elapsed + initialValue;
|
9 |
-
}
|
10 |
-
return (-amountOfChange / 2) * (--elapsed * (elapsed - 2) - 1) + initialValue;
|
11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/stage.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import { easeInOutQuad } from "./
|
2 |
import { onDriverClick } from "./events";
|
3 |
import { trigger } from "./hooks";
|
4 |
import { getConfig } from "./config";
|
|
|
1 |
+
import { easeInOutQuad } from "./utils";
|
2 |
import { onDriverClick } from "./events";
|
3 |
import { trigger } from "./hooks";
|
4 |
import { getConfig } from "./config";
|
src/utils.ts
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { getConfig } from "./config";
|
2 |
+
|
3 |
+
export function easeInOutQuad(
|
4 |
+
elapsed: number,
|
5 |
+
initialValue: number,
|
6 |
+
amountOfChange: number,
|
7 |
+
duration: number
|
8 |
+
): number {
|
9 |
+
if ((elapsed /= duration / 2) < 1) {
|
10 |
+
return (amountOfChange / 2) * elapsed * elapsed + initialValue;
|
11 |
+
}
|
12 |
+
return (-amountOfChange / 2) * (--elapsed * (elapsed - 2) - 1) + initialValue;
|
13 |
+
}
|
14 |
+
|
15 |
+
export function bringInView(element: Element) {
|
16 |
+
if (!element || isElementInView(element)) {
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
const shouldSmoothScroll = getConfig("smoothScroll");
|
21 |
+
|
22 |
+
element.scrollIntoView({
|
23 |
+
// Removing the smooth scrolling for elements which exist inside the scrollable parent
|
24 |
+
// This was causing the highlight to not properly render
|
25 |
+
behavior:
|
26 |
+
!shouldSmoothScroll || hasScrollableParent(element) ? "auto" : "smooth",
|
27 |
+
inline: "center",
|
28 |
+
block: "center",
|
29 |
+
});
|
30 |
+
}
|
31 |
+
|
32 |
+
function hasScrollableParent(e: Element) {
|
33 |
+
if (!e || !e.parentElement) {
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
+
const parent = e.parentElement as HTMLElement & { scrollTopMax?: number };
|
38 |
+
|
39 |
+
return parent.scrollHeight > parent.clientHeight;
|
40 |
+
}
|
41 |
+
|
42 |
+
function isElementInView(element: Element) {
|
43 |
+
const rect = element.getBoundingClientRect();
|
44 |
+
|
45 |
+
return (
|
46 |
+
rect.top >= 0 &&
|
47 |
+
rect.left >= 0 &&
|
48 |
+
rect.bottom <=
|
49 |
+
(window.innerHeight || document.documentElement.clientHeight) &&
|
50 |
+
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
51 |
+
);
|
52 |
+
}
|