Post

Trigger Tricks: Running Google Apps Scripts on Form Submit, Edit, or a Timer

Trigger Tricks: Running Google Apps Scripts on Form Submit, Edit, or a Timer

The real power of Google Apps Script isn’t just in what your scripts can do—it’s in when they run. Using triggers, you can have your scripts execute automatically at specific times or in response to certain events, turning manual processes into fully automated workflows.

In this guide, I’ll walk you through everything you need to know about Google Apps Script triggers, from simple time-based scheduling to advanced event-driven automation.

What Are Triggers?

Triggers are mechanisms that automatically run your Apps Script functions when certain events occur. Think of them as the “when” for your script’s “what”—they determine when your code executes without you having to manually run it.

Types of Triggers

Google Apps Script offers two main categories of triggers:

Simple Triggers

These run automatically without requiring special authorization:

  • onOpen(): Runs when a user opens a spreadsheet, document, or form
  • onEdit(): Runs when a user edits a spreadsheet
  • onFormSubmit(): Runs when a form is submitted
  • onInstall(): Runs when a user installs an add-on

Installable Triggers

These require explicit setup and can handle more complex scenarios:

  • Time-driven triggers: Run at specific times or intervals
  • Spreadsheet triggers: Include open, edit, form submit, and change events
  • Form triggers: Run on form submit
  • Calendar triggers: Run when events are created, updated, or deleted

Let’s explore how to implement each type with practical examples.

Time-Driven Triggers: Schedule Scripts to Run Automatically

Time-driven triggers let you run scripts at specific times or intervals—perfect for daily reports, monthly summaries, or any recurring task.

Example: Daily Email Report

This script sends a daily summary report from a Google Sheet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function sendDailyReport() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("Sales");
  const dataRange = sheet.getRange("A2:D");
  const data = dataRange.getValues();
  
  // Calculate today's totals
  let todayTotal = 0;
  const today = new Date();
  today.setHours(0, 0, 0, 0); // Set to beginning of today
  
  for (let i = 0; i < data.length; i++) {
    if (data[i][0] === "") continue; // Skip empty rows
    
    // Convert date string to Date object and check if it's today
    const rowDate = new Date(data[i][0]);
    rowDate.setHours(0, 0, 0, 0);
    
    if (rowDate.getTime() === today.getTime()) {
      todayTotal += Number(data[i][3] || 0); // Column D contains sale amounts
    }
  }
  
  // Format currency
  const formattedTotal = Utilities.formatString('$%.2f', todayTotal);
  
  // Compose and send email
  const recipient = "your.email@example.com";
  const subject = `Daily Sales Report: ${today.toDateString()}`;
  const body = `
    Daily Sales Summary for ${today.toDateString()}
    
    Total Sales Today: ${formattedTotal}
    
    This is an automated report from your Google Sheet.
  `;
  
  GmailApp.sendEmail(recipient, subject, body);
}

Setting Up the Time Trigger

  1. Open your Google Sheet and go to Extensions → Apps Script
  2. Paste the above code and save
  3. Click on the clock icon (Triggers) in the left sidebar
  4. Click Add Trigger and configure:
    • Function to run: sendDailyReport
    • Event source: Time-driven
    • Type of time: Day timer
    • Time of day: 6am to 7am
  5. Click Save

Now your report will automatically send every morning between 6-7am.

Form Submit Triggers: React to New Submissions

Form submit triggers run your code whenever someone submits a Google Form—perfect for automated responses, data processing, or notifications.

Example: Custom Confirmation Emails

This script sends personalized thank-you emails to form respondents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function sendThankYouEmail(e) {
  // e is the form submit event object
  const responses = e.response.getItemResponses();
  let email = "";
  let name = "";
  
  // Extract email and name from form responses
  for (let i = 0; i < responses.length; i++) {
    const question = responses[i].getItem().getTitle();
    const answer = responses[i].getResponse();
    
    if (question === "Email Address") {
      email = answer;
    } else if (question === "Your Name") {
      name = answer;
    }
  }
  
  // Only proceed if we have a valid email
  if (email) {
    const subject = "Thank you for your submission";
    const body = `
      Dear ${name},
      
      Thank you for completing our form. We've received your information and will process it shortly.
      
      Best regards,
      Your Organization
    `;
    
    GmailApp.sendEmail(email, subject, body);
  }
}

Spreadsheet Edit Triggers: React to Changes

Edit triggers run when users modify your spreadsheet, allowing you to validate data, update related cells, or track changes.

Example: Automatic Timestamp on Edit

This script adds a timestamp whenever someone modifies specific columns:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function onEdit(e) {
  // e is the edit event object
  const sheet = e.source.getActiveSheet();
  const range = e.range;
  
  // Only run if edits happen in columns B, C, or D (indexes 1, 2, or 3)
  if (range.getColumn() >= 2 && range.getColumn() <= 4) {
    // Add timestamp in column E (index 4) of the same row
    const row = range.getRow();
    const timestampCell = sheet.getRange(row, 5);
    
    // Only update if this is a data row (not the header)
    if (row > 1) {
      timestampCell.setValue(new Date());
      
      // Format as date+time
      timestampCell.setNumberFormat("yyyy-MM-dd HH:mm:ss");
    }
  }
}

Debugging and Monitoring Triggers

When working with triggers, robust error handling is crucial because you won’t be there to see errors as they happen.

Example: Error Handling in a Triggered Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function weeklyReport() {
  try {
    // Your code here
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName("Data");
    
    // More processing...
    
    // Send email with report
    GmailApp.sendEmail("manager@example.com", "Weekly Report", "Report data...");
    
    // Log success
    console.log("Weekly report sent successfully on " + new Date());
  } catch (error) {
    // Log error details
    console.error("Error in weeklyReport: " + error.message);
    
    // Send notification about the error
    GmailApp.sendEmail(
      "admin@example.com",
      "Error in Weekly Report Script",
      "The weekly report script failed with error: " + error.message
    );
  }
}

Trigger Limitations and Best Practices

Limitations

  • Quotas: Free Google accounts have daily quotas for trigger executions
  • Duration: A script must complete within 6 minutes (or 30 minutes for certain operations)
  • Frequency: Time-driven triggers can run at most once per minute

Best Practices

  1. Keep scripts efficient: Optimize code to run as quickly as possible
  2. Use error handling: Always wrap triggered code in try/catch blocks
  3. Implement logging: Log important events and errors
  4. Set up failure notifications: Get alerted when your triggers fail
  5. Test thoroughly: Manual testing before setting up triggers saves headaches

Conclusion

Triggers transform Google Apps Script from a handy coding tool into a powerful automation platform. By configuring your scripts to run automatically based on time or events, you can create sophisticated workflows that save time and reduce errors.

Whether you’re maintaining a simple inventory system or creating complex approval processes, mastering triggers unlocks the full potential of Google Apps Script.

Next Steps

Ready to level up your Google Apps Script skills further? Check out these other tutorials:

What automation challenges are you facing? Share in the comments, and I might feature solutions in upcoming tutorials!


Questions? Corrections? Issues and pull requests are always welcome.

This post is licensed under CC BY 4.0 by the author.