or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-loading.mddataflow.mdevents.mdexpressions.mdindex.mdparsing.mdscales.mdscenegraph.mdstatistics.mdtime.mdutilities.mdview.md

events.mddocs/

0

# Event Handling

1

2

Vega's event system provides comprehensive interaction support with event parsing, selection handling, listener management, and integration with the dataflow system for reactive visualizations.

3

4

## Capabilities

5

6

### Event Selector Parsing

7

8

Event selector string parsing for interaction specifications.

9

10

```typescript { .api }

11

/**

12

* Parse event selector strings into structured event selectors

13

* @param selector - Event selector string or array of strings

14

* @returns Array of parsed event selector objects

15

*/

16

function parseSelector(selector: string | string[]): EventSelector[];

17

18

interface EventSelector {

19

/** Event source (view, window, etc.) */

20

source?: EventSource;

21

22

/** Event type (click, mouseover, etc.) */

23

type: string;

24

25

/** Marks to target */

26

markname?: string;

27

28

/** Mark type to target */

29

marktype?: string;

30

31

/** CSS selector for DOM events */

32

selector?: string;

33

34

/** Event filter expression */

35

filter?: string;

36

37

/** Throttle delay in milliseconds */

38

throttle?: number;

39

40

/** Debounce delay in milliseconds */

41

debounce?: number;

42

43

/** Between event handling */

44

between?: EventSelector[];

45

46

/** Consume event flag */

47

consume?: boolean;

48

}

49

50

type EventSource = 'view' | 'scope' | 'window' | '*' | string;

51

```

52

53

### View Event Handling

54

55

View-level event listener management for scenegraph events.

56

57

```typescript { .api }

58

/**

59

* View event listener management interface

60

*/

61

interface ViewEventHandling {

62

/**

63

* Add event listener for scenegraph events

64

* @param type - Event type or selector

65

* @param handler - Event handler function

66

* @returns View instance for method chaining

67

*/

68

addEventListener(type: string | EventSelector, handler: EventListenerHandler): View;

69

70

/**

71

* Remove event listener

72

* @param type - Event type or selector

73

* @param handler - Event handler function to remove

74

* @returns View instance for method chaining

75

*/

76

removeEventListener(type: string | EventSelector, handler: EventListenerHandler): View;

77

78

/**

79

* Add signal change listener

80

* @param name - Signal name to watch

81

* @param handler - Signal change handler

82

* @returns View instance for method chaining

83

*/

84

addSignalListener(name: string, handler: SignalListenerHandler): View;

85

86

/**

87

* Remove signal change listener

88

* @param name - Signal name

89

* @param handler - Handler to remove

90

* @returns View instance for method chaining

91

*/

92

removeSignalListener(name: string, handler: SignalListenerHandler): View;

93

94

/**

95

* Add data change listener

96

* @param name - Dataset name to watch

97

* @param handler - Data change handler

98

* @returns View instance for method chaining

99

*/

100

addDataListener(name: string, handler: DataListenerHandler): View;

101

102

/**

103

* Remove data change listener

104

* @param name - Dataset name

105

* @param handler - Handler to remove

106

* @returns View instance for method chaining

107

*/

108

removeDataListener(name: string, handler: DataListenerHandler): View;

109

110

/**

111

* Add resize listener

112

* @param handler - Resize handler function

113

* @returns View instance for method chaining

114

*/

115

addResizeListener(handler: ResizeHandler): View;

116

117

/**

118

* Remove resize listener

119

* @param handler - Handler to remove

120

* @returns View instance for method chaining

121

*/

122

removeResizeListener(handler: ResizeHandler): View;

123

124

/**

125

* Get event stream for specified parameters

126

* @param source - Event source

127

* @param type - Event type

128

* @param filter - Optional event filter

129

* @returns Event stream

130

*/

131

events(source: EventSource, type: string, filter?: Function): EventStream;

132

}

133

134

type EventListenerHandler = (event: ScenegraphEvent, item?: Item | null) => void;

135

type SignalListenerHandler = (name: string, value: SignalValue) => void;

136

type DataListenerHandler = (name: string, changeset: Changeset) => void;

137

type ResizeHandler = (width: number, height: number) => void;

138

139

interface ScenegraphEvent extends Event {

140

/** Event type */

141

type: string;

142

143

/** Scene graph item */

144

item?: Item;

145

146

/** Vega event metadata */

147

vega?: EventMetadata;

148

}

149

150

interface EventMetadata {

151

/** View instance */

152

view: View;

153

154

/** Source item */

155

item: Item | null;

156

157

/** Event coordinates */

158

x?: number;

159

y?: number;

160

}

161

162

interface Item {

163

/** Item data */

164

datum: any;

165

166

/** Mark definition */

167

mark: RuntimeMark;

168

169

/** Item bounds */

170

bounds?: Bounds;

171

}

172

173

type SignalValue = any;

174

175

interface Changeset {

176

insert: any[];

177

remove: any[];

178

modify: any[];

179

}

180

```

181

182

### Event Stream System

183

184

Event stream management for reactive event handling.

185

186

```typescript { .api }

187

/**

188

* Event stream for managing event flow and transformations

189

*/

190

class EventStream {

191

/**

192

* Create new event stream

193

* @param source - Event source

194

* @param type - Event type

195

* @param filter - Optional event filter function

196

*/

197

constructor(source?: EventSource, type?: string, filter?: Function);

198

199

/** Event source */

200

source: EventSource;

201

202

/** Event type */

203

type: string;

204

205

/** Event filter */

206

filter: Function;

207

208

/** Stream targets */

209

targets: Set<any>;

210

211

/**

212

* Filter events with predicate

213

* @param predicate - Filter predicate function

214

* @returns New filtered event stream

215

*/

216

filter(predicate: (event: any) => boolean): EventStream;

217

218

/**

219

* Throttle event stream

220

* @param delay - Throttle delay in milliseconds

221

* @returns New throttled event stream

222

*/

223

throttle(delay: number): EventStream;

224

225

/**

226

* Debounce event stream

227

* @param delay - Debounce delay in milliseconds

228

* @returns New debounced event stream

229

*/

230

debounce(delay: number): EventStream;

231

232

/**

233

* Map events to new values

234

* @param mapper - Event mapping function

235

* @returns New mapped event stream

236

*/

237

map(mapper: (event: any) => any): EventStream;

238

239

/**

240

* Merge with another event stream

241

* @param stream - Stream to merge with

242

* @returns New merged event stream

243

*/

244

merge(stream: EventStream): EventStream;

245

246

/**

247

* Take events between start and end streams

248

* @param start - Start event stream

249

* @param end - End event stream

250

* @returns New between event stream

251

*/

252

between(start: EventStream, end: EventStream): EventStream;

253

254

/**

255

* Apply stream to target

256

* @param target - Target object or function

257

* @returns The event stream instance

258

*/

259

apply(target: any): EventStream;

260

}

261

```

262

263

### Event Types

264

265

Built-in event type definitions and constants.

266

267

```typescript { .api }

268

/** Mouse event types */

269

interface MouseEvents {

270

/** Mouse click */

271

click: 'click';

272

273

/** Mouse double click */

274

dblclick: 'dblclick';

275

276

/** Mouse down */

277

mousedown: 'mousedown';

278

279

/** Mouse up */

280

mouseup: 'mouseup';

281

282

/** Mouse move */

283

mousemove: 'mousemove';

284

285

/** Mouse enter */

286

mouseenter: 'mouseenter';

287

288

/** Mouse leave */

289

mouseleave: 'mouseleave';

290

291

/** Mouse over */

292

mouseover: 'mouseover';

293

294

/** Mouse out */

295

mouseout: 'mouseout';

296

297

/** Context menu */

298

contextmenu: 'contextmenu';

299

300

/** Mouse wheel */

301

wheel: 'wheel';

302

}

303

304

/** Touch event types */

305

interface TouchEvents {

306

/** Touch start */

307

touchstart: 'touchstart';

308

309

/** Touch end */

310

touchend: 'touchend';

311

312

/** Touch move */

313

touchmove: 'touchmove';

314

315

/** Touch cancel */

316

touchcancel: 'touchcancel';

317

}

318

319

/** Keyboard event types */

320

interface KeyboardEvents {

321

/** Key down */

322

keydown: 'keydown';

323

324

/** Key up */

325

keyup: 'keyup';

326

327

/** Key press */

328

keypress: 'keypress';

329

}

330

331

/** Window event types */

332

interface WindowEvents {

333

/** Window resize */

334

resize: 'resize';

335

336

/** Window scroll */

337

scroll: 'scroll';

338

339

/** Page load */

340

load: 'load';

341

342

/** Before unload */

343

beforeunload: 'beforeunload';

344

}

345

346

/** View event types */

347

interface ViewEvents {

348

/** View resize */

349

'view:resize': 'view:resize';

350

351

/** View render */

352

'view:render': 'view:render';

353

354

/** View update */

355

'view:update': 'view:update';

356

}

357

358

/** Timer event types */

359

interface TimerEvents {

360

/** Timer tick */

361

timer: 'timer';

362

}

363

```

364

365

### Interaction Utilities

366

367

Utility functions for common interaction patterns.

368

369

```typescript { .api }

370

/**

371

* Configure hover behavior for marks

372

* @param view - View instance

373

* @param hoverSet - Encoding set for hover state

374

* @param leaveSet - Encoding set for leave state

375

* @returns Configured view

376

*/

377

function configureHover(view: View, hoverSet?: string, leaveSet?: string): View;

378

379

/**

380

* Configure tooltip behavior

381

* @param view - View instance

382

* @param tooltipHandler - Custom tooltip handler

383

* @returns Configured view

384

*/

385

function configureTooltip(view: View, tooltipHandler?: TooltipHandler): View;

386

387

/**

388

* Configure selection behavior

389

* @param view - View instance

390

* @param selectionConfig - Selection configuration

391

* @returns Configured view

392

*/

393

function configureSelection(view: View, selectionConfig: SelectionConfig): View;

394

395

type TooltipHandler = (handler: any, event: MouseEvent, item: Item, value: any) => void;

396

397

interface SelectionConfig {

398

/** Selection type */

399

type: 'point' | 'interval' | 'multi';

400

401

/** Selection fields */

402

fields?: string[];

403

404

/** Selection encoding */

405

encodings?: string[];

406

407

/** Selection signal binding */

408

bind?: SelectionBinding;

409

410

/** Selection initialization */

411

init?: any;

412

413

/** Selection event trigger */

414

on?: string | EventSelector[];

415

416

/** Selection clear trigger */

417

clear?: string | EventSelector[];

418

419

/** Selection resolve mode */

420

resolve?: 'global' | 'union' | 'intersect';

421

}

422

423

interface SelectionBinding {

424

/** Input element type */

425

input: string;

426

427

/** Element binding properties */

428

[prop: string]: any;

429

}

430

```

431

432

### Event Debugging

433

434

Event debugging and inspection utilities.

435

436

```typescript { .api }

437

/**

438

* Enable event debugging for a view

439

* @param view - View instance

440

* @param options - Debug options

441

*/

442

function enableEventDebugging(view: View, options?: EventDebugOptions): void;

443

444

/**

445

* Log event information

446

* @param event - Event to log

447

* @param context - Additional context information

448

*/

449

function logEvent(event: Event, context?: any): void;

450

451

/**

452

* Trace event propagation

453

* @param view - View instance

454

* @param eventType - Event type to trace

455

*/

456

function traceEvents(view: View, eventType: string): void;

457

458

interface EventDebugOptions {

459

/** Log all events */

460

logAll?: boolean;

461

462

/** Event types to log */

463

types?: string[];

464

465

/** Include event data */

466

includeData?: boolean;

467

468

/** Custom log function */

469

logger?: (message: string, data?: any) => void;

470

}

471

```

472

473

## Usage Examples

474

475

### Basic Event Listeners

476

477

```typescript

478

import { View, parseSelector } from "vega";

479

480

// Add click listener

481

view.addEventListener('click', (event, item) => {

482

if (item && item.datum) {

483

console.log('Clicked on:', item.datum);

484

}

485

});

486

487

// Add hover listeners

488

view.addEventListener('mouseover', (event, item) => {

489

if (item) {

490

console.log('Hovering:', item.datum);

491

}

492

});

493

494

view.addEventListener('mouseout', (event, item) => {

495

console.log('Mouse left item');

496

});

497

```

498

499

### Signal Listeners

500

501

```typescript

502

// Listen to signal changes

503

view.addSignalListener('selectedCategory', (name, value) => {

504

console.log(`Signal ${name} changed to:`, value);

505

506

// Update other visualizations or UI

507

updateRelatedCharts(value);

508

});

509

510

// Listen to multiple signals

511

['filter', 'sort', 'groupBy'].forEach(signalName => {

512

view.addSignalListener(signalName, (name, value) => {

513

console.log(`Configuration ${name}:`, value);

514

});

515

});

516

```

517

518

### Data Change Listeners

519

520

```typescript

521

// Monitor data changes

522

view.addDataListener('sales', (name, changeset) => {

523

console.log(`Dataset ${name} changed:`, {

524

inserted: changeset.insert.length,

525

removed: changeset.remove.length,

526

modified: changeset.modify.length

527

});

528

529

// Trigger external updates

530

if (changeset.insert.length > 0) {

531

notifyNewData(changeset.insert);

532

}

533

});

534

```

535

536

### Complex Event Selectors

537

538

```typescript

539

import { parseSelector } from "vega";

540

541

// Parse complex selector

542

const selectors = parseSelector([

543

'rect:click',

544

'symbol:mouseover',

545

'@legend:click',

546

'window:resize'

547

]);

548

549

selectors.forEach(selector => {

550

console.log('Parsed selector:', selector);

551

});

552

553

// Use with view

554

view.addEventListener('rect:click', (event, item) => {

555

console.log('Rectangle clicked:', item.datum);

556

});

557

558

// Mark-specific events

559

view.addEventListener('symbol:dblclick', (event, item) => {

560

console.log('Symbol double-clicked:', item.datum);

561

});

562

```

563

564

### Event Streams

565

566

```typescript

567

import { EventStream } from "vega";

568

569

// Create event stream

570

const clickStream = view.events('view', 'click');

571

572

// Filter and transform events

573

const filteredClicks = clickStream

574

.filter(event => event.item && event.item.mark.marktype === 'rect')

575

.throttle(100); // Throttle to max 10 clicks per second

576

577

// Apply to signals

578

filteredClicks.apply((event) => {

579

view.signal('lastClicked', event.item.datum.id).run();

580

});

581

582

// Merge multiple streams

583

const mouseEvents = view.events('view', 'mousemove')

584

.merge(view.events('view', 'click'));

585

586

mouseEvents.apply((event) => {

587

view.signal('mouseActivity', Date.now()).run();

588

});

589

```

590

591

### Interaction Patterns

592

593

```typescript

594

// Hover highlighting

595

view.addEventListener('mouseover', (event, item) => {

596

if (item && item.mark.marktype === 'rect') {

597

view.signal('hoveredItem', item.datum.id).run();

598

}

599

});

600

601

view.addEventListener('mouseout', (event, item) => {

602

view.signal('hoveredItem', null).run();

603

});

604

605

// Click selection

606

let selectedItems = new Set();

607

608

view.addEventListener('click', (event, item) => {

609

if (item) {

610

const id = item.datum.id;

611

612

if (event.shiftKey) {

613

// Multi-select with Shift

614

if (selectedItems.has(id)) {

615

selectedItems.delete(id);

616

} else {

617

selectedItems.add(id);

618

}

619

} else {

620

// Single select

621

selectedItems.clear();

622

selectedItems.add(id);

623

}

624

625

view.signal('selectedItems', Array.from(selectedItems)).run();

626

}

627

});

628

```

629

630

### Custom Event Handling

631

632

```typescript

633

// Custom tooltip handler

634

const customTooltip = (handler, event, item, value) => {

635

if (item && item.datum) {

636

const tooltip = document.getElementById('custom-tooltip');

637

tooltip.innerHTML = `

638

<strong>${item.datum.name}</strong><br>

639

Value: ${item.datum.value}<br>

640

Category: ${item.datum.category}

641

`;

642

tooltip.style.left = event.pageX + 10 + 'px';

643

tooltip.style.top = event.pageY + 10 + 'px';

644

tooltip.style.display = 'block';

645

}

646

};

647

648

// Hide tooltip on mouse out

649

view.addEventListener('mouseout', () => {

650

document.getElementById('custom-tooltip').style.display = 'none';

651

});

652

653

view.tooltip(customTooltip);

654

```

655

656

### Keyboard Interactions

657

658

```typescript

659

// Keyboard navigation

660

view.addEventListener('keydown', (event) => {

661

const currentSelection = view.signal('selectedIndex');

662

const dataLength = view.data('dataset').length;

663

664

switch (event.key) {

665

case 'ArrowUp':

666

event.preventDefault();

667

view.signal('selectedIndex',

668

Math.max(0, currentSelection - 1)).run();

669

break;

670

671

case 'ArrowDown':

672

event.preventDefault();

673

view.signal('selectedIndex',

674

Math.min(dataLength - 1, currentSelection + 1)).run();

675

break;

676

677

case 'Enter':

678

event.preventDefault();

679

// Trigger selection action

680

const selectedData = view.data('dataset')[currentSelection];

681

console.log('Selected:', selectedData);

682

break;

683

}

684

});

685

```

686

687

### Event Throttling and Debouncing

688

689

```typescript

690

// Throttled mouse tracking

691

let lastMouseUpdate = 0;

692

const MOUSE_THROTTLE = 16; // ~60fps

693

694

view.addEventListener('mousemove', (event) => {

695

const now = Date.now();

696

if (now - lastMouseUpdate > MOUSE_THROTTLE) {

697

lastMouseUpdate = now;

698

699

view.signal('mouseX', event.x)

700

.signal('mouseY', event.y)

701

.run();

702

}

703

});

704

705

// Debounced search

706

let searchTimeout;

707

const SEARCH_DEBOUNCE = 300;

708

709

view.addSignalListener('searchQuery', (name, value) => {

710

clearTimeout(searchTimeout);

711

712

searchTimeout = setTimeout(() => {

713

// Perform search operation

714

const filtered = performSearch(value);

715

view.data('searchResults', filtered).run();

716

}, SEARCH_DEBOUNCE);

717

});

718

```

719

720

### Window and View Events

721

722

```typescript

723

// Responsive resize handling

724

view.addResizeListener((width, height) => {

725

console.log(`View resized to ${width}x${height}`);

726

727

// Update responsive breakpoints

728

const isSmall = width < 600;

729

view.signal('isSmallScreen', isSmall).run();

730

731

// Adjust layout parameters

732

if (isSmall) {

733

view.signal('fontSize', 10)

734

.signal('padding', 20)

735

.run();

736

} else {

737

view.signal('fontSize', 12)

738

.signal('padding', 40)

739

.run();

740

}

741

});

742

743

// Window event handling

744

window.addEventListener('beforeunload', (event) => {

745

// Save view state before page unload

746

const state = view.getState();

747

localStorage.setItem('viewState', JSON.stringify(state));

748

});

749

750

// Restore state on load

751

window.addEventListener('load', () => {

752

const savedState = localStorage.getItem('viewState');

753

if (savedState) {

754

view.setState(JSON.parse(savedState));

755

}

756

});

757

```

758

759

### Event Delegation

760

761

```typescript

762

// Event delegation for dynamic content

763

view.addEventListener('click', (event, item) => {

764

if (!item) return;

765

766

// Delegate based on mark type

767

switch (item.mark.marktype) {

768

case 'rect':

769

handleBarClick(item);

770

break;

771

772

case 'symbol':

773

handlePointClick(item);

774

break;

775

776

case 'text':

777

handleLabelClick(item);

778

break;

779

780

default:

781

console.log('Unhandled mark type:', item.mark.marktype);

782

}

783

});

784

785

function handleBarClick(item) {

786

console.log('Bar clicked:', item.datum);

787

// Drill down or filter logic

788

}

789

790

function handlePointClick(item) {

791

console.log('Point clicked:', item.datum);

792

// Detail view logic

793

}

794

795

function handleLabelClick(item) {

796

console.log('Label clicked:', item.datum);

797

// Edit or info logic

798

}

799

```