onEdit trigger
A function named onEdit(e) runs automatically whenever a user edits a cell. e.range is the edited Range, e.value the new value, e.oldValue the previous one and e.source the spreadsheet. Check the sheet name and column first; the simple trigger cannot send email or open other files, so install an onEdit trigger for that.
Syntax
function onEdit(e) { const range = e.range; const sheet = range.getSheet(); ... }| Part | What it is |
|---|---|
e.range | The edited Range; use getRow(), getColumn(), getSheet() |
e.value | New value as a string; undefined when several cells changed or a cell was cleared |
e.oldValue | Previous value, only for single-cell edits |
e.source | The Spreadsheet being edited |
Examples
function onEdit(e) {
const sheet = e.range.getSheet();
if (sheet.getName() !== "Tasks" || e.range.getColumn() !== 3) return;
sheet.getRange(e.range.getRow(), 4).setValue(new Date());
}Result: Editing column C on Tasks writes a timestamp into column D of that row
function onEdit(e) {
if (e.range.getA1Notation() === "B2" && e.value === "Done") {
e.range.setBackground("#d9ead3");
}
}Result: B2 turns green when set to Done
function onEdit(e) {
if (e.range.getColumn() === 1 && e.range.getRow() > 1) {
e.range.setValue(String(e.range.getValue()).trim().toUpperCase());
}
}Result: Everything typed in column A becomes trimmed uppercase
Common mistakes
- Running onEdit from the editor throws Cannot read properties of undefined (reading range): e is only provided by a real edit; test by editing the sheet, or pass a fake object in a test function.
- The trigger fires on every tab and every column: add the sheet name and column guards at the top, before any work.
- GmailApp or openById fails inside onEdit: the simple trigger runs with limited authorization; create an installable onEdit trigger in the Triggers panel instead.
- e.value is undefined after pasting or deleting: multi-cell edits have no value; read e.range.getValues() instead.
Practice onEdit trigger
These drills check your answer on live data, free, no account needed.
- Run only onceReturn a function that calls `fn` the first time it is called, with that call's arguments, and returns `fn`'s result. Every later call returns that same first result without calling `fn` again, even when the result was `undefined` or `0`.
- DebounceReturn a debounced version of `fn`: calling it schedules `fn` to run `wait` ms later with the latest arguments, and every new call before then restarts the wait. So a burst of calls runs `fn` once, after the burst goes quiet. Tests run on a fake clock: `setTimeout`, `clearTimeout` and `Date.now` are simulated, so waits are instant and exact.
Questions
Does onEdit fire when a formula changes its result?
No. It only fires for user edits, not for formula recalculation, imports or changes made by other scripts.
Why does onEdit not fire for another user?
Simple onEdit runs for anyone editing with edit rights. An installable trigger runs as the person who installed it, so their permissions apply.
Can onEdit send an email?
Only as an installable trigger: Triggers, Add Trigger, choose your function, event source From spreadsheet, event type On edit.
Related
- Installable triggersInstallable triggers in Google Apps Script: how they differ from simple triggers, creating them from the UI or ScriptApp.newTrigger, onChange, onFormSubmit.
- onOpen and custom menusAdd a custom menu to Google Sheets with Apps Script onOpen: createMenu, addItem, separators and submenus that run your functions, and why a menu may hide.
- Time-driven triggersSchedule Google Apps Script with time-driven triggers: create them in the Triggers panel or with ScriptApp.newTrigger, run daily, hourly or weekly, delete.
- Send email from a sheetSend email from Google Sheets with Apps Script: MailApp.sendEmail and GmailApp.sendEmail with HTML body, cc and attachments, loop a list of recipients, quota.
Previous: Performance and batchingNext: onOpen and custom menus