Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article describes important concepts related to working with events in Excel and provides code samples that show how to register event handlers, handle events, and remove event handlers using the Excel JavaScript API.
Key points
- Excel fires event notifications when specific changes occur in the workbook.
- Register event handlers using the
addmethod on event objects likeworksheet.onChanged. - Remove event handlers using the
removemethod with the sameRequestContext. - Event handlers don't persist across sessions and are destroyed when the add-in refreshes or closes.
- Disable events temporarily using
context.runtime.enableEventsto improve performance during batch operations.
Events in Excel
Each time certain types of changes occur in an Excel workbook, an event notification fires. By using the Excel JavaScript API, you can register event handlers that allow your add-in to automatically run a designated function when a specific event occurs. The following events are currently supported.
| Event | Description | Supported objects |
|---|---|---|
onActivated |
Occurs when an object is activated. | Chart, ChartCollection, Shape, Worksheet, WorksheetCollection |
onActivated |
Occurs when a workbook is activated. | Workbook |
onAdded |
Occurs when an object is added to the collection. | ChartCollection, CommentCollection, TableCollection, WorksheetCollection |
onAutoSaveSettingChanged |
Occurs when the autoSave setting is changed on the workbook. |
Workbook |
onCalculated |
Occurs when a worksheet has finished calculation (or all the worksheets of the collection have finished). | Worksheet, WorksheetCollection |
onChanged |
Occurs when the data of individual cells or comments has changed. | CommentCollection, Table, TableCollection, Worksheet, WorksheetCollection |
onColumnSorted |
Occurs when one or more columns have been sorted. This happens as the result of a left-to-right sort operation. | Worksheet, WorksheetCollection |
onDataChanged |
Occurs when data or formatting within the binding is changed. | Binding |
onDeactivated |
Occurs when an object is deactivated. | Chart, ChartCollection, Shape, Worksheet, WorksheetCollection |
onDeleted |
Occurs when an object is deleted from the collection. | ChartCollection, CommentCollection, TableCollection, WorksheetCollection |
onFormatChanged |
Occurs when the format is changed on a worksheet. | Worksheet, WorksheetCollection |
onFormulaChanged |
Occurs when a formula is changed. | Worksheet, WorksheetCollection |
onMoved |
Occurs when a worksheet is moved within a workbook. | WorksheetCollection |
onNameChanged |
Occurs when the worksheet name is changed. | Worksheet, WorksheetCollection |
onProtectionChanged |
Occurs when the worksheet protection state is changed. | Worksheet, WorksheetCollection |
onRowHiddenChanged |
Occurs when the row-hidden state changes on a specific worksheet. This event doesn't fire when rows are hidden or shown by advanced filters. See Known limitations. | Worksheet, WorksheetCollection |
onRowSorted |
Occurs when one or more rows have been sorted. This happens as the result of a top-to-bottom sort operation. | Worksheet, WorksheetCollection |
onSelectionChanged |
Occurs when the active cell or selected range is changed. | Binding, Table, Workbook, Worksheet, WorksheetCollection |
onSettingsChanged |
Occurs when the Settings in the document are changed. | SettingCollection |
onSingleClicked |
Occurs when left-clicked/tapped action occurs in the worksheet. | Worksheet, WorksheetCollection |
onVisibilityChanged |
Occurs when the worksheet visibility is changed. | Worksheet, WorksheetCollection |
Events in preview
Note
The following events are currently available only in public preview. To use this feature, you must use the preview version of the Office JavaScript API library from the Office.js content delivery network (CDN). The type definition file for TypeScript compilation and IntelliSense is found at the CDN and DefinitelyTyped. You can install these types with npm install --save-dev @types/office-js-preview.
For more information on our upcoming APIs, please visit Excel JavaScript API requirement sets.
| Event | Description | Supported objects |
|---|---|---|
onFiltered |
Occurs when a filter is applied to an object. | Table, TableCollection, Worksheet, WorksheetCollection |
Event triggers
Events within an Excel workbook can be triggered by:
- User interaction via the Excel user interface (UI) that changes the workbook
- Office Add-in (JavaScript) code that changes the workbook
- VBA add-in (macro) code that changes the workbook
Any change that complies with default behavior of Excel will trigger the corresponding events in a workbook.
Lifecycle of an event handler
An event handler is created when an add-in registers the event handler. It is destroyed when the add-in unregisters the event handler or when the add-in is refreshed, reloaded, or closed. Event handlers do not persist as part of the Excel file, or across sessions with Excel on the web.
Caution
When an object to which events are registered is deleted (e.g., a table with an onChanged event registered), the event handler no longer triggers but remains in memory until the add-in or Excel session refreshes or closes.
Events and coauthoring
With coauthoring, multiple people can work together and edit the same Excel workbook simultaneously. For events that can be triggered by a coauthor, such as onChanged, the corresponding Event object will contain a source property that indicates whether the event was triggered locally by the current user (event.source == Local) or was triggered by the remote coauthor (event.source == Remote).
Register an event handler
The following code sample registers an event handler for the onChanged event in the worksheet named Sample. The code specifies that when data changes in that worksheet, the handleChange function should run.
await Excel.run(async (context) => {
const worksheet = context.workbook.worksheets.getItem("Sample");
worksheet.onChanged.add(handleChange);
await context.sync();
console.log("Event handler successfully registered for onChanged event in the worksheet.");
}).catch(errorHandlerFunction);
Handle an event
As shown in the previous example, when you register an event handler, you indicate the function that should run when the specified event occurs. You can design that function to perform whatever actions your scenario requires. The following code sample shows an event handler function that simply writes information about the event to the console.
async function handleChange(event) {
await Excel.run(async (context) => {
await context.sync();
console.log("Change type of event: " + event.changeType);
console.log("Address of event: " + event.address);
console.log("Source of event: " + event.source);
}).catch(errorHandlerFunction);
}
Remove an event handler
The following code sample registers an event handler for the onSelectionChanged event in the worksheet named Sample and defines the handleSelectionChange function that will run when the event occurs. It also defines the remove() function that can subsequently be called to remove that event handler. Note that the RequestContext used to create the event handler is needed to remove it.
let eventResult;
async function run() {
await Excel.run(async (context) => {
const worksheet = context.workbook.worksheets.getItem("Sample");
eventResult = worksheet.onSelectionChanged.add(handleSelectionChange);
await context.sync();
console.log("Event handler successfully registered for onSelectionChanged event in the worksheet.");
});
}
async function handleSelectionChange(event) {
await Excel.run(async (context) => {
await context.sync();
console.log("Address of current selection: " + event.address);
});
}
async function remove() {
// The `RequestContext` used to create the event handler is needed to remove it.
// In this example, `eventContext` is being used to keep track of that context.
await Excel.run(eventResult.context, async (context) => {
eventResult.remove();
await context.sync();
eventResult = null;
console.log("Event handler successfully removed.");
});
}
Enable and disable events
The performance of an add-in may be improved by disabling events. For example, your app might never need to receive events, or it could ignore events while performing batch-edits of multiple entities.
Events are enabled and disabled at the runtime level.
The enableEvents property determines if events are fired and their handlers are activated.
The following code sample shows how to toggle events on and off.
await Excel.run(async (context) => {
context.runtime.load("enableEvents");
await context.sync();
let eventBoolean = !context.runtime.enableEvents;
context.runtime.enableEvents = eventBoolean;
if (eventBoolean) {
console.log("Events are currently on.");
} else {
console.log("Events are currently off.");
}
await context.sync();
});
Known limitations
onRowHiddenChanged doesn't fire for advanced filters
The onRowHiddenChanged event doesn't fire when rows are hidden or shown as a result of applying an advanced filter. It fires correctly for standard worksheet filters and manual row hide or show operations.
As a workaround, poll the rowHidden property on a set of rows at a regular interval to detect visibility changes caused by advanced filters. The following code sample demonstrates this approach.
// Global variables to manage the polling mechanism.
let pollTimer = null;
const rowHiddenSnapshot = new Map(); // Stores previous row visibility states.
async function startTracking() {
// Prevent multiple timers from running simultaneously.
if (pollTimer) {
console.log("Already tracking.");
return;
}
console.log("Starting row visibility tracking...");
// Poll every 500ms - adjust this interval based on your performance needs.
pollTimer = window.setInterval(() => {
pollRowHiddenDiff();
}, 500);
}
function stopTracking() {
// Clean up the polling mechanism.
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
rowHiddenSnapshot.clear(); // Clear the snapshot to free memory.
console.log("Stopped tracking.");
}
}
async function pollRowHiddenDiff() {
try {
await Excel.run(async (context) => {
// Get the active worksheet and its used range.
const sheet = context.workbook.worksheets.getActiveWorksheet();
const usedRange = sheet.getUsedRange();
usedRange.load("rowIndex,rowCount"); // Load properties we need.
await context.sync();
// Get the starting row (0-based) and limit the number of rows to check.
const startRow = usedRange.rowIndex;
const maxRows = Math.min(usedRange.rowCount, 200); // Limit to 200 rows for performance.
// Build an array of row ranges to check.
const rows = [];
for (let i = 0; i < maxRows; i++) {
// Convert to 1-based row number for Excel range addressing.
const row1Based = startRow + i + 1;
// Create a range for the entire row (e.g., "5:5" for row 5).
const row = sheet.getRange(`${row1Based}:${row1Based}`);
row.load("rowHidden"); // Load the rowHidden property.
rows.push(row);
}
// Sync to get all the rowHidden values.
await context.sync();
// Arrays to track which rows changed visibility.
const hidden = [];
const unhidden = [];
// Compare current visibility state with previous snapshot.
for (let i = 0; i < rows.length; i++) {
const rowIndex = startRow + i; // 0-based row index for internal tracking.
const current = rows[i].rowHidden; // Current visibility state.
const previous = rowHiddenSnapshot.get(rowIndex); // Previous state from snapshot.
// If this is the first time we're checking this row, just store its state.
if (previous === undefined) {
rowHiddenSnapshot.set(rowIndex, current);
continue;
}
// If the visibility state changed, record it and update the snapshot.
if (previous !== current) {
rowHiddenSnapshot.set(rowIndex, current);
// Convert to 1-based row numbers for user-friendly logging.
if (current) {
hidden.push(rowIndex + 1); // Row became hidden.
} else {
unhidden.push(rowIndex + 1); // Row became visible.
}
}
}
// Log any detected changes.
if (hidden.length || unhidden.length) {
console.log("Row visibility changed:", { hidden, unhidden });
}
});
} catch (error) {
// Handle errors gracefully to prevent polling from stopping.
console.error("Error during row visibility polling:", error);
// If the error is severe (e.g., worksheet deleted), consider stopping tracking.
if (error.code === "ItemNotFound") {
console.log("Worksheet no longer exists, stopping tracking.");
stopTracking();
}
}
}
See also
Office Add-ins