File size: 13,985 Bytes
e8faad4
0aebd1a
bceba74
937c1ad
39bb1e9
937c1ad
 
df9c1b5
 
 
cce1e72
bceba74
 
0aebd1a
bceba74
cce1e72
 
bceba74
0aebd1a
bceba74
 
eaa24bf
 
 
 
 
 
 
 
bceba74
 
 
f3a59d2
1f3be9a
05ee342
 
88df282
05ee342
32ebc11
05ee342
1f3be9a
 
32ebc11
06b7251
5ce9fcf
1f3be9a
06b7251
1f3be9a
eaa24bf
5ce9fcf
eaa24bf
15b4fce
5ce9fcf
eaa24bf
15b4fce
1f3be9a
32ebc11
1f3be9a
 
eaa24bf
1f3be9a
a3fea2b
 
 
 
 
 
64ff0a9
 
 
eaa24bf
 
64ff0a9
eaa24bf
64ff0a9
 
f3a59d2
a3fea2b
d060f14
a3fea2b
32ebc11
a3fea2b
 
 
eaa24bf
a3fea2b
eaa24bf
1a68d34
a3fea2b
d060f14
a3fea2b
32ebc11
a3fea2b
 
 
 
eaa24bf
a3fea2b
eaa24bf
 
 
a3fea2b
 
 
 
 
f3a59d2
a3fea2b
32ebc11
a3fea2b
 
 
 
eaa24bf
a3fea2b
eaa24bf
 
8c52c69
 
fc53067
eaa24bf
 
a3fea2b
 
 
c51fe58
 
 
 
 
eaa24bf
c51fe58
eaa24bf
 
 
c51fe58
 
 
eaa24bf
c51fe58
 
 
f3a59d2
a3fea2b
6eba7f5
a3fea2b
 
 
 
 
 
 
eaa24bf
a3fea2b
eaa24bf
 
 
 
 
a3fea2b
 
eaa24bf
a3fea2b
eaa24bf
 
 
 
a3fea2b
 
eaa24bf
a3fea2b
eaa24bf
 
 
 
a3fea2b
 
 
 
 
 
eaa24bf
32ebc11
937c1ad
 
 
f3a59d2
c5e62d3
7ee9e88
c5e62d3
 
 
 
 
 
 
eaa24bf
c5e62d3
eaa24bf
 
 
 
c5e62d3
 
eaa24bf
c5e62d3
eaa24bf
 
 
c5e62d3
 
7ee9e88
c5e62d3
eaa24bf
7ee9e88
 
 
 
 
eaa24bf
c5e62d3
 
eaa24bf
c5e62d3
eaa24bf
 
 
 
c5e62d3
 
 
 
 
 
eaa24bf
c5e62d3
 
 
 
56b910a
937c1ad
1a68d34
937c1ad
 
 
32ebc11
937c1ad
 
 
eaa24bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
937c1ad
 
eaa24bf
43c0911
937c1ad
 
 
32ebc11
937c1ad
 
 
eaa24bf
 
 
 
 
 
 
 
 
 
 
 
140aae9
eaa24bf
 
937c1ad
 
 
32ebc11
56b910a
 
 
 
 
 
32ebc11
56b910a
 
 
 
eaa24bf
 
 
 
56b910a
 
 
937c1ad
 
32ebc11
937c1ad
 
 
 
56b910a
 
eaa24bf
56b910a
 
 
eaa24bf
 
 
 
 
 
56b910a
c5e62d3
 
 
 
56b910a
eaa24bf
56b910a
f907594
 
 
56b910a
 
f907594
a43a05e
 
 
 
eaa24bf
56b910a
 
eaa24bf
 
56b910a
937c1ad
 
56b910a
937c1ad
56b910a
 
937c1ad
56b910a
7ee7519
eaa24bf
 
937c1ad
eaa24bf
937c1ad
 
96c5c02
 
32ebc11
6f3b9a7
937c1ad
 
8bd3bfd
 
32ebc11
8bd3bfd
1cdd696
 
32ebc11
1cdd696
f23af63
1cdd696
937c1ad
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
<h1 align="center"><img height="150" src="./.github/images/driver.svg" /><br> Driver.js</h1>

<p align="center">
  <a href="https://github.com/kamranahmedse/driver.js/blob/master/license">
    <img src="https://img.shields.io/badge/License-MIT-yellow.svg" />
  </a>
  <a href="https://npmjs.org/package/driver.js">
    <img src="https://img.shields.io/npm/v/driver.js.svg" alt="version" />
  </a>
  <a href="https://npmjs.org/package/driver.js">
    <img src="https://img.shields.io/npm/dt/driver.js.svg" alt="downloads" />
  </a>
</p>

<p align="center">
  <b>Powerful, highly customizable vanilla JavaScript engine to drive the user's focus across the page</b></br>
  <sub>No external dependencies, supports all major browsers and highly customizable <sub>
</p>

<br />

- **Simple**: is simple to use and has no external dependency at all
- **Highly customizable**: has a powerful API and can be used however you want
- **Highlight anything**: highlight any (literally any) element on page
- **Feature introductions**: create powerful feature introductions for your web applications
- **Focus shifters**: add focus shifters for users
- **User friendly**: Everything is controllable by keyboard
- **Consistent behavior**: usable across all browsers (including in-famous IE)
- **MIT Licensed**: free for personal and commercial use

![](./demo/images/split.png)

For Usage and Examples, [have a look at demo](http://kamranahmed.info/driver.js)

## So, yet another tour library?

**No**, it is not. **Tours are just one of the many use-cases**. Driver.js can be used wherever you need some sort of overlay for the page; some common usecases could be: e.g. dimming the background when user is interacting with some component i.e. [the way Facebook does](https://i.imgur.com/Q3PzaKkr.png) when you try to create a post, using it as a focus shifter to bring user's attention to some component on page, or using it to simulate those "Turn off the Lights" widgets that you might have seen on video players online, etc.

Driver.js is written in Vanilla JS, has zero dependencies and is highly customizable. It has several options allowing you to manipulate how it behaves and also **provides you the hooks** to manipulate the elements as they are highlighted, about to be highlighted, or deselected.

## Installation

You can install it using `yarn` or `npm`, whatever you prefer.

```bash
yarn add driver.js
npm install driver.js
```

Or include it using CDN. If you want a specific version, put it as `[email protected]` in the name

```html
<script src="https://unpkg.com/driver.js/dist/driver.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/driver.js/dist/driver.min.css" />
```

Or grab the code from `dist` directory and include it directly.

```html
<link rel="stylesheet" href="/dist/driver.min.css" />
<script src="/dist/driver.min.js"></script>
```

![](./demo/images/split.png)

## Usage and Demo

If you are using some sort of module bundler, import the library and the CSS file

```javascript
import Driver from "driver.js";
import "driver.js/dist/driver.min.css";
```

otherwise use the `script` and `link` tags to import the JavaScript and CSS files.

Demos and many more usage examples can be found [in the docs page](http://kamranahmed.info/driver.js).

### Highlighting Single Element – [Demo](http://kamranahmed.info/driver.js#single-element-no-popover)

You can highlight a single element by simply passing the selector.

```javascript
const driver = new Driver();
driver.highlight("#create-post");
```

A real world usage example for this is: using it to dim the background and highlight the required element e.g. the way Facebook does it when creating a post.

### Highlight and Popover – [Demo](http://kamranahmed.info/driver.js#single-element-with-popover)

You can show additional details beside the highlighted element using the popover.

```javascript
const driver = new Driver();
driver.highlight({
  element: "#some-element",
  popover: {
    title: "Title for the Popover",
    description: "Description for it",
  },
});
```

Also, `title` and `description` can have HTML as well.

### Positioning the Popover – [Demo](http://kamranahmed.info/driver.js#single-element-with-popover-position)

By default, driver automatically finds the suitable position for the popover and displays it. You can override it using `position` property.

```javascript
const driver = new Driver();
driver.highlight({
  element: "#some-element",
  popover: {
    title: "Title for the Popover",
    description: "Description for it",
    // position can be left, left-center, left-bottom, top,
    // top-center, top-right, right, right-center, right-bottom,
    // bottom, bottom-center, bottom-right, mid-center
    position: "left",
  },
});
```

You can also add offset to the popover position by using the `offset` property

```javascript
const driver = new Driver();
driver.highlight({
  element: "#some-element",
  popover: {
    title: "Title for the Popover",
    description: "Description for it",
    position: "bottom",
    // Will show it 20 pixels away from the actual position of popover
    // You may also provide the negative values
    offset: 20,
  },
});
```

### Creating Feature Introductions – [Demo](http://kamranahmed.info/driver.js)

Feature introductions are helpful when onboarding new users and giving them an idea about different parts of the application; you can create them seamlessly with Driver. Define the steps and call the `start` when you want to start presenting. User will be able to control the steps using the keyboard or using the buttons on popovers.

```javascript
const driver = new Driver();

// Define the steps for introduction
driver.defineSteps([
  {
    element: "#first-element-introduction",
    popover: {
      className: "first-step-popover-class",
      title: "Title on Popover",
      description: "Body of the popover",
      position: "left",
    },
  },
  {
    element: "#second-element-introduction",
    popover: {
      title: "Title on Popover",
      description: "Body of the popover",
      position: "top",
    },
  },
  {
    element: "#third-element-introduction",
    popover: {
      title: "Title on Popover",
      description: "Body of the popover",
      position: "right",
    },
  },
]);

// Start the introduction
driver.start();
```

You can also hide the buttons and control the introductions programmatically by using the API methods listed below.

![](./demo/images/split.png)

### Asynchronous Actions – [Demo](http://kamranahmed.info/driver.js)

For any asynchronous actions between the transition steps, you may delay the execution till the action completes. All you have to do is stop the transition using `driver.preventMove()` in your `onNext` or `onPrevious` callbacks and initiate it manually using `driver.moveNext()`. Here is a sample implementation where it will stop at the second step for four seconds and then move on to the next step.

```javascript
const driver = new Driver();

// Define the steps for introduction
driver.defineSteps([
  {
    element: "#first-element-introduction",
    popover: {
      title: "Title on Popover",
      description: "Body of the popover",
      position: "left",
    },
  },
  {
    element: "#second-element-introduction",
    popover: {
      title: "Title on Popover",
      description: "Body of the popover",
      position: "top",
    },
    onNext: () => {
      // Prevent moving to the next step
      driver.preventMove();

      // Perform some action or create the element to move to
      // And then move to that element
      setTimeout(() => {
        driver.moveNext();
      }, 4000);
    },
  },
  {
    element: "#third-element-introduction",
    popover: {
      title: "Title on Popover",
      description: "Body of the popover",
      position: "right",
    },
  },
]);

// Start the introduction
driver.start();
```

You can also hide the buttons and control the introductions programmatically by using the API methods listed below.

![](./demo/images/split.png)

## API

Driver comes with several options that you can manipulate to make Driver behave as you like

### Driver Definition

Here are the options that Driver understands:

```javascript
const driver = new Driver({
  className: "scoped-class", // className to wrap driver.js popover
  animate: true, // Whether to animate or not
  opacity: 0.75, // Background opacity (0 means only popovers and without overlay)
  padding: 10, // Distance of element from around the edges
  allowClose: true, // Whether the click on overlay should close or not
  overlayClickNext: false, // Whether the click on overlay should move next
  doneBtnText: "Done", // Text on the final button
  closeBtnText: "Close", // Text on the close button for this step
  stageBackground: "#ffffff", // Background color for the staged behind highlighted element
  nextBtnText: "Next", // Next button text for this step
  prevBtnText: "Previous", // Previous button text for this step
  showButtons: false, // Do not show control buttons in footer
  keyboardControl: true, // Allow controlling through keyboard (escape to close, arrow keys to move)
  scrollIntoViewOptions: {}, // We use `scrollIntoView()` when possible, pass here the options for it if you want any
  onHighlightStarted: Element => {}, // Called when element is about to be highlighted
  onHighlighted: Element => {}, // Called when element is fully highlighted
  onDeselected: Element => {}, // Called when element has been deselected
  onReset: Element => {}, // Called when overlay is about to be cleared
  onNext: Element => {}, // Called when moving to next step on any step
  onPrevious: Element => {}, // Called when moving to previous step on any step
});
```

Note that all the button options that you provide in the driver definition can be overridden for a specific step by giving them in the step definition

### Step Definition

Here are the set of options that you can pass while defining steps `defineSteps` or the object that you pass to `highlight` method:

```javascript
const stepDefinition = {
  element: "#some-item", // Query selector string or Node to be highlighted
  stageBackground: "#ffffff", // This will override the one set in driver
  popover: {
    // There will be no popover if empty or not given
    className: "popover-class", // className to wrap this specific step popover in addition to the general className in Driver options
    title: "Title", // Title on the popover
    description: "Description", // Body of the popover
    showButtons: false, // Do not show control buttons in footer
    doneBtnText: "Done", // Text on the last button
    closeBtnText: "Close", // Text on the close button
    nextBtnText: "Next", // Next button text
    prevBtnText: "Previous", // Previous button text
  },
  onNext: () => {}, // Called when moving to next step from current step
  onPrevious: () => {}, // Called when moving to previous step from current step
};
```

For example, here is how it would look when highlighting a single element:

```javascript
const driver = new Driver(driverOptions);
driver.highlight(stepDefinition);
```

And this is how it would look when creating a step by step guide:

```javascript
const driver = new Driver(driverOptions);
driver.defineSteps([
  stepDefinition1,
  stepDefinition2,
  stepDefinition3,
  stepDefinition4,
]);
```

### API Methods

Below are the set of methods that are available:

```javascript
const driver = new Driver(driverOptions);

// Checks if the driver is active or not
if (driver.isActivated) {
  console.log("Driver is active");
}

// In case of the steps guide, you can call below methods
driver.defineSteps([stepDefinition1, stepDefinition2, stepDefinition3]);
driver.start((stepNumber = 0)); // Starts driving through the defined steps
driver.moveNext(); // Moves to next step in the steps list
driver.movePrevious(); // Moves to previous step in the steps list
driver.hasNextStep(); // Checks if there is next step to move to
driver.hasPreviousStep(); // Checks if there is previous step to move to

// Prevents the current move. Useful in `onNext` or `onPrevious` if you want to
// perform some asynchronous task and manually move to next step
driver.preventMove();

// Highlights the element using query selector or the step definition
driver.highlight(string | stepDefinition);

// Reposition the popover and highlighted element
driver.refresh();

// Resets the overlay and clears the screen
driver.reset();

// Additionally you can pass a boolean parameter
// to clear immediately and not do the animations etc
// Could be useful when you, let's say, want to run
// a different instance of driver while one was running
driver.reset((clearImmediately = false));

// Checks if there is any highlighted element
if (driver.hasHighlightedElement()) {
  console.log("There is an element highlighted");
}

// Gets the currently highlighted element on screen
// It would be an instance of `/src/core/element.js`
const activeElement = driver.getHighlightedElement();

// Gets the last highlighted element, would be an instance of `/src/core/element.js`
const lastActiveElement = driver.getLastHighlightedElement();

activeElement.getCalculatedPosition(); // Gets screen co-ordinates of the active element
activeElement.hidePopover(); // Hide the popover
activeElement.showPopover(); // Show the popover

activeElement.getNode(); // Gets the DOM Element behind this element
```

![](./demo/images/split.png)

**Note –** Do not forget to add `e.stopPropagation()` to the `click` binding that triggers driver.

![](./demo/images/split.png)

## Contributions

Feel free to submit pull requests, create issues or spread the word.

## Sponsored By

Thanks to [BrowserStack](https://browserstack.com) for sponsoring the compatibility testing needs.

[![BrowserStack](./demo/images/browserstack.png)](https://www.browserstack.com)

## License

MIT &copy; [Kamran Ahmed](https://twitter.com/kamranahmedse)