CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-cytoscape

Graph theory library for analysis and visualisation of network data with interactive rendering capabilities

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

event-system.mddocs/

Event System

Rich event system supporting user interactions, element lifecycle events, viewport changes, and custom events with delegation, namespacing, and promise-based handling.

Capabilities

Event Binding and Handling

Bind event handlers to core instance and element collections.

interface Core {
  /**
   * Bind event handler
   * @param events - Space-separated event names
   * @param selector - Optional element selector for delegation
   * @param handler - Event handler function
   * @returns Core instance for chaining
   */
  on(events: string, selector?: string, handler?: EventHandler): Core;
  on(events: string, handler: EventHandler): Core;
  
  /**
   * Bind one-time event handler
   * @param events - Space-separated event names
   * @param selector - Optional element selector for delegation
   * @param handler - Event handler function
   * @returns Core instance for chaining
   */
  one(events: string, selector?: string, handler?: EventHandler): Core;
  one(events: string, handler: EventHandler): Core;
  
  /**
   * Unbind event handler
   * @param events - Event names (optional, unbinds all if not specified)
   * @param selector - Element selector (optional)
   * @param handler - Handler function (optional, unbinds all matching if not specified)
   * @returns Core instance for chaining
   */
  off(events?: string, selector?: string, handler?: EventHandler): Core;
  off(events?: string, handler?: EventHandler): Core;
  
  /**
   * Trigger event
   * @param events - Space-separated event names
   * @param extraParams - Additional parameters to pass to handlers
   * @returns Core instance for chaining
   */
  emit(events: string, extraParams?: any[]): Core;
  
  /**
   * Promise-based event binding (resolves on first event)
   * @param events - Space-separated event names
   * @returns Promise that resolves when event occurs
   */
  pon(events: string): Promise<Event>;
  
  /**
   * Remove event listener (alias for off)
   * @param events - Event names
   * @param selector - Element selector
   * @param handler - Handler function
   * @returns Core instance for chaining
   */
  removeListener(events?: string, selector?: string, handler?: EventHandler): Core;
}

interface Collection {
  /**
   * Bind event handler to collection elements
   * @param events - Space-separated event names
   * @param handler - Event handler function
   * @returns Collection for chaining
   */
  on(events: string, handler: EventHandler): Collection;
  
  /**
   * Bind one-time event handler
   * @param events - Space-separated event names
   * @param handler - Event handler function
   * @returns Collection for chaining
   */
  one(events: string, handler: EventHandler): Collection;
  
  /**
   * Unbind event handler
   * @param events - Event names (optional)
   * @param handler - Handler function (optional)
   * @returns Collection for chaining
   */
  off(events?: string, handler?: EventHandler): Collection;
  
  /**
   * Trigger event on collection elements
   * @param events - Space-separated event names
   * @param extraParams - Additional parameters
   * @returns Collection for chaining
   */
  trigger(events: string, extraParams?: any[]): Collection;
  
  /**
   * Promise-based event binding
   * @param events - Space-separated event names
   * @returns Promise that resolves when event occurs
   */
  pon(events: string): Promise<Event>;
}

interface EventHandler {
  (event: Event): void;
}

Event Object

Event object passed to event handlers contains contextual information.

interface Event {
  /**
   * Element that triggered the event
   */
  target: Collection;
  
  /**
   * Event type name
   */
  type: string;
  
  /**
   * Model position where event occurred
   */
  position: Position;
  
  /**
   * Rendered (screen) position where event occurred
   */
  renderedPosition: Position;
  
  /**
   * Cytoscape instance
   */
  cy: Core;
  
  /**
   * Original browser event (for user events)
   */
  originalEvent?: MouseEvent | TouchEvent | KeyboardEvent;
  
  /**
   * Namespace for the event
   */
  namespace?: string;
  
  /**
   * Additional data passed with event
   */
  data?: any;
  
  /**
   * Timestamp when event occurred
   */
  timeStamp: number;
}

User Input Events

Events triggered by user interactions with mouse, touch, and keyboard.

/**
 * Mouse events
 */
type MouseEventType = 
  | "mousedown"      // Mouse button pressed
  | "mouseup"        // Mouse button released
  | "click"          // Mouse click
  | "dblclick"       // Double click
  | "mouseover"      // Mouse enters element
  | "mouseout"       // Mouse leaves element
  | "mousemove"      // Mouse moves over element
  | "rightclick";    // Right mouse button click

/**
 * Touch events
 */
type TouchEventType = 
  | "touchstart"     // Touch begins
  | "touchmove"      // Touch moves
  | "touchend";      // Touch ends

/**
 * Normalized gesture events (work for both mouse and touch)
 */
type GestureEventType = 
  | "tap"            // Single tap/click
  | "tapstart"       // Tap/click begins
  | "tapend"         // Tap/click ends
  | "tapdrag"        // Drag during tap
  | "taphold"        // Long press/hold
  | "cxttap";        // Context tap (right-click or long tap)

/**
 * Keyboard events
 */
type KeyboardEventType = 
  | "keydown"        // Key pressed down
  | "keyup"          // Key released
  | "keypress";      // Key pressed and released

Usage Examples:

// Basic click handler
cy.on('tap', 'node', function(evt) {
  const node = evt.target;
  console.log('Tapped node:', node.id());
  console.log('Position:', evt.position);
});

// Mouse hover effects
cy.on('mouseover', 'node', function(evt) {
  evt.target.style('background-color', 'red');
});

cy.on('mouseout', 'node', function(evt) {
  evt.target.removeStyle('background-color');
});

// Touch handling
cy.on('taphold', 'node', function(evt) {
  console.log('Node held for long press');
});

// Keyboard shortcuts
cy.on('keydown', function(evt) {
  if (evt.originalEvent.key === 'Delete') {
    cy.$(':selected').remove();
  }
});

Collection Events

Events related to element lifecycle and state changes.

/**
 * Lifecycle events
 */
type LifecycleEventType = 
  | "add"            // Element added to graph
  | "remove"         // Element removed from graph
  | "move";          // Element moved to different location

/**
 * Selection events
 */
type SelectionEventType = 
  | "select"         // Element selected programmatically
  | "unselect"       // Element unselected programmatically
  | "tapselect"      // Element selected by user tap
  | "tapunselect"    // Element unselected by user tap
  | "boxselect"      // Elements selected by box selection
  | "boxunselect";   // Elements unselected by box selection

/**
 * Interaction events
 */
type InteractionEventType = 
  | "grab"           // Node grabbed for dragging
  | "drag"           // Node being dragged
  | "free"           // Node released from dragging
  | "lock"           // Node locked (position fixed)
  | "unlock";        // Node unlocked

/**
 * Data events
 */
type DataEventType = 
  | "data"           // Element data changed
  | "scratch"        // Element scratch data changed
  | "style"          // Element style changed
  | "position";      // Element position changed

/**
 * Class events
 */
type ClassEventType = 
  | "class";         // Element classes changed

Usage Examples:

// Handle element additions
cy.on('add', 'node', function(evt) {
  const node = evt.target;
  console.log('Node added:', node.id());
  
  // Apply default styling to new nodes
  node.style('background-color', '#666');
});

// Track selection changes
cy.on('select', 'node', function(evt) {
  console.log('Selected:', evt.target.id());
});

cy.on('unselect', function(evt) {
  console.log('Unselected:', evt.target.id());
});

// Handle dragging
cy.on('grab', 'node', function(evt) {
  evt.target.style('background-color', 'red');
});

cy.on('free', 'node', function(evt) {
  evt.target.removeStyle('background-color');
});

// Monitor data changes
cy.on('data', function(evt) {
  console.log('Data changed on:', evt.target.id());
});

Graph Events

Events related to the graph as a whole, including viewport and layout changes.

/**
 * Viewport events
 */
type ViewportEventType = 
  | "pan"            // Viewport panned
  | "zoom"           // Viewport zoomed
  | "viewport";      // Viewport changed (pan or zoom)

/**
 * Layout events
 */
type LayoutEventType = 
  | "layoutstart"    // Layout started running
  | "layoutready"    // Layout positions calculated
  | "layoutstop";    // Layout finished running

/**
 * Core events
 */
type CoreEventType = 
  | "ready"          // Graph fully initialized
  | "destroy"        // Graph instance destroyed
  | "render"         // Graph rendered/redrawn
  | "resize";        // Container resized

Usage Examples:

// Handle viewport changes
cy.on('zoom', function(evt) {
  console.log('Zoom level:', cy.zoom());
});

cy.on('pan', function(evt) {
  console.log('Pan position:', cy.pan());
});

// Layout progress tracking
cy.on('layoutstart', function(evt) {
  console.log('Layout started:', evt.layout.options.name);
  // Show loading indicator
});

cy.on('layoutstop', function(evt) {
  console.log('Layout finished');
  // Hide loading indicator
});

// Graph initialization
cy.on('ready', function(evt) {
  console.log('Graph ready');
  // Perform initial setup
});

Event Namespacing

Use namespaces to organize and manage event handlers.

/**
 * Namespaced event syntax: "eventname.namespace"
 */

// Bind namespaced events
cy.on('tap.myPlugin', 'node', handler1);
cy.on('tap.myPlugin', 'edge', handler2);
cy.on('select.myPlugin', handler3);

// Unbind all events in namespace
cy.off('.myPlugin');

// Unbind specific namespaced event
cy.off('tap.myPlugin');

// Trigger namespaced event
cy.emit('customEvent.myNamespace');

Event Delegation

Use selectors to handle events on specific element types without binding to each element.

// Event delegation - handles all current and future nodes
cy.on('tap', 'node', function(evt) {
  // This handler applies to all nodes, even ones added later
  console.log('Node tapped:', evt.target.id());
});

// Multiple selectors
cy.on('tap', 'node.important, edge.highlighted', function(evt) {
  console.log('Important element tapped');
});

// Complex selectors
cy.on('tap', 'node[type="user"]', function(evt) {
  console.log('User node tapped');
});

Promise-Based Event Handling

Use promises for one-time event handling and async workflows.

/**
 * Promise-based event binding
 */

// Wait for next tap event
cy.pon('tap').then(function(evt) {
  console.log('Tap occurred on:', evt.target.id());
});

// Wait for layout completion
cy.pon('layoutstop').then(function() {
  console.log('Layout finished');
  // Continue with next step
});

// Async/await syntax
async function waitForSelection() {
  const evt = await cy.pon('select');
  console.log('Selected:', evt.target.id());
  return evt.target;
}

// Chain promise events
cy.pon('tap')
  .then(() => cy.pon('layoutstop'))
  .then(() => {
    console.log('Tap and layout both completed');
  });

Custom Events

Create and trigger custom events for application-specific logic.

// Trigger custom events
cy.emit('myCustomEvent', [{ customData: 'value' }]);

// Listen for custom events
cy.on('myCustomEvent', function(evt, data) {
  console.log('Custom event triggered with:', data);
});

// Element-specific custom events
const node = cy.$('#myNode');
node.trigger('nodeSpecificEvent', [{ nodeData: node.data() }]);

node.on('nodeSpecificEvent', function(evt, data) {
  console.log('Node event with data:', data);
});

Event Performance and Best Practices

Optimize event handling for better performance.

// Use event delegation instead of binding to many elements
// Good:
cy.on('tap', 'node', handler);

// Avoid:
cy.nodes().on('tap', handler);

// Batch event bindings
cy.batch(function() {
  cy.on('tap', 'node', handler1);
  cy.on('tap', 'edge', handler2);
  cy.on('select', handler3);
});

// Clean up event handlers when done
function cleanup() {
  cy.off('.myApp');  // Remove all namespaced handlers
}

// Throttle or debounce high-frequency events
let panTimeout;
cy.on('pan', function() {
  clearTimeout(panTimeout);
  panTimeout = setTimeout(function() {
    // Handle pan end
    console.log('Pan finished');
  }, 100);
});

Event Bubbling and Propagation

Understand event flow and control propagation.

// Events bubble from target to parent elements
cy.on('tap', 'node', function(evt) {
  console.log('Node tapped');
  
  // Stop event from bubbling to parent
  evt.stopPropagation();
});

// Core-level handler (receives all events)
cy.on('tap', function(evt) {
  console.log('Something was tapped:', evt.target);
});

// Event order: specific selector handlers first, then general handlers

Install with Tessl CLI

npx tessl i tessl/npm-cytoscape

docs

core-management.md

element-collections.md

event-system.md

extensions.md

graph-algorithms.md

index.md

layout-system.md

styling-system.md

tile.json