Interactive label functionality with event listeners for click, hover, enter, and leave events enabling dynamic label behavior and user interactions.
Attach interactive behaviors to data labels with context-aware event handling.
interface EventOptions {
/** Event listener configuration */
listeners?: {
/** Called when a label is clicked */
click?: Listener;
/** Called when mouse enters a label */
enter?: Listener;
/** Called when mouse leaves a label */
leave?: Listener;
};
}
type Listener = (context: Context, event: ChartEvent) => boolean | void;
interface Context {
active: boolean; // Whether element is currently hovered
chart: Chart; // Chart instance
dataIndex: number; // Index of the data point
dataset: ChartDataset; // Dataset containing the data
datasetIndex: number; // Index of the dataset
}Return Values:
true: Update the label and re-render the chartfalse or undefined: No action neededUsage Examples:
// Basic click handler
datalabels: {
listeners: {
click: function(context, event) {
console.log('Clicked:', context.dataset.data[context.dataIndex]);
return false; // No chart update needed
}
}
}
// Hover effects
datalabels: {
backgroundColor: 'lightblue',
listeners: {
enter: function(context, event) {
// Change to hover color
context.backgroundColor = 'darkblue';
return true; // Trigger chart update
},
leave: function(context, event) {
// Reset to default color
context.backgroundColor = 'lightblue';
return true; // Trigger chart update
}
}
}Handle user clicks on data labels for interactive functionality.
Usage Examples:
// Data point selection
datalabels: {
listeners: {
click: function(context, event) {
const chart = context.chart;
const dataIndex = context.dataIndex;
const datasetIndex = context.datasetIndex;
// Toggle data point visibility
const meta = chart.getDatasetMeta(datasetIndex);
meta.data[dataIndex].hidden = !meta.data[dataIndex].hidden;
chart.update();
return false;
}
}
}
// External callbacks
datalabels: {
listeners: {
click: function(context, event) {
// Call external function with data
window.handleDataPointClick({
value: context.dataset.data[context.dataIndex],
label: context.dataset.label,
dataIndex: context.dataIndex,
datasetIndex: context.datasetIndex
});
return false;
}
}
}
// Drill-down functionality
datalabels: {
listeners: {
click: function(context, event) {
const value = context.dataset.data[context.dataIndex];
const label = context.chart.data.labels[context.dataIndex];
// Navigate to detail view
window.location.href = `/details/${label}/${value}`;
return false;
}
}
}Create interactive hover effects that respond to mouse movements.
Usage Examples:
// Highlight on hover
datalabels: {
color: 'black',
font: { weight: 'normal' },
listeners: {
enter: function(context, event) {
// Highlight style
context.color = 'red';
context.font.weight = 'bold';
return true;
},
leave: function(context, event) {
// Reset style
context.color = 'black';
context.font.weight = 'normal';
return true;
}
}
}
// Dynamic content on hover
datalabels: {
formatter: function(value, context) {
if (context.active) {
// Show detailed info when hovered
const percentage = ((value / context.dataset.data.reduce((a, b) => a + b)) * 100).toFixed(1);
return `${value} (${percentage}%)`;
}
return value;
},
listeners: {
enter: function(context, event) {
return true; // Trigger re-render to show detailed format
},
leave: function(context, event) {
return true; // Trigger re-render to show simple format
}
}
}
// Tooltip-like behavior
datalabels: {
display: false, // Hidden by default
listeners: {
enter: function(context, event) {
context.display = true;
return true;
},
leave: function(context, event) {
context.display = false;
return true;
}
}
}Complex interactive behaviors using event combinations.
Multi-State Interactions:
// Track selection state
let selectedPoints = new Set();
datalabels: {
backgroundColor: function(context) {
const key = `${context.datasetIndex}-${context.dataIndex}`;
return selectedPoints.has(key) ? 'orange' : 'lightblue';
},
listeners: {
click: function(context, event) {
const key = `${context.datasetIndex}-${context.dataIndex}`;
if (selectedPoints.has(key)) {
selectedPoints.delete(key);
} else {
selectedPoints.add(key);
}
return true; // Update to reflect selection state
},
enter: function(context, event) {
const key = `${context.datasetIndex}-${context.dataIndex}`;
if (!selectedPoints.has(key)) {
context.backgroundColor = 'yellow'; // Hover state
return true;
}
return false;
},
leave: function(context, event) {
const key = `${context.datasetIndex}-${context.dataIndex}`;
if (!selectedPoints.has(key)) {
context.backgroundColor = 'lightblue'; // Reset
return true;
}
return false;
}
}
}Conditional Event Handling:
// Different behaviors based on data
datalabels: {
listeners: {
click: function(context, event) {
const value = context.dataset.data[context.dataIndex];
if (value > 100) {
// High value action
showDetailModal(context);
} else if (value > 50) {
// Medium value action
highlightRelatedData(context);
} else {
// Low value action
showWarning('Value is quite low');
}
return false;
}
}
}Event Delegation:
// Centralized event handling
function createEventHandler(action) {
return function(context, event) {
// Log all interactions
console.log(`${action} on`, {
dataset: context.dataset.label,
value: context.dataset.data[context.dataIndex],
index: context.dataIndex
});
// Dispatch to appropriate handler
switch (action) {
case 'click':
handleClick(context, event);
break;
case 'enter':
handleHover(context, event, true);
break;
case 'leave':
handleHover(context, event, false);
break;
}
return true;
};
}
datalabels: {
listeners: {
click: createEventHandler('click'),
enter: createEventHandler('enter'),
leave: createEventHandler('leave')
}
}Cross-Chart Interactions:
// Sync multiple charts
datalabels: {
listeners: {
enter: function(context, event) {
// Highlight corresponding data in other charts
const otherCharts = getAllCharts().filter(c => c !== context.chart);
otherCharts.forEach(chart => {
highlightDataPoint(chart, context.dataIndex);
});
return false;
},
leave: function(context, event) {
// Clear highlights in other charts
const otherCharts = getAllCharts().filter(c => c !== context.chart);
otherCharts.forEach(chart => {
clearHighlights(chart);
});
return false;
}
}
}Optimize event handling for smooth interactions.
Debounced Events:
// Debounce hover events for performance
let hoverTimeout;
datalabels: {
listeners: {
enter: function(context, event) {
clearTimeout(hoverTimeout);
hoverTimeout = setTimeout(() => {
// Delayed hover action
performExpensiveHoverAction(context);
}, 150);
return false;
},
leave: function(context, event) {
clearTimeout(hoverTimeout);
return false;
}
}
}Event Throttling:
// Throttle frequent events
let lastEventTime = 0;
const EVENT_THROTTLE = 100; // ms
datalabels: {
listeners: {
enter: function(context, event) {
const now = Date.now();
if (now - lastEventTime < EVENT_THROTTLE) {
return false;
}
lastEventTime = now;
// Handle event
return handleHoverEvent(context, event);
}
}
}