Recently, I thought it would be a cool idea to add a date-time stamp to the end of a Google Doc checklist item with Google Apps Script. So I knew when I completed a task.
I often share a project Google Doc with clients and then add my tasks to the document list. With Google’s new check box list item, I wanted to add the date and time that I completed the task when I checked the box.
The bad news is that there is no onEdit() trigger (like in Google Sheets) for the DocumentApp class that would listen for an edit of the document and see a change of the checked box from unchecked to checked and then apply the date-time stamp. 😢
All good, I settled for the next best thing! A menu item.
Take a quick look at the results.
Table of Contents
What the Date-Time Stamp Button does
The timestamp button allows you to set your cursor anywhere on a:
- List Item
- Paragraph
- Table cell item
- Header
… and then click the ‘AddDTS’ menu item. The Google Apps Script will then run the script and your custom date time stamp will be added.
The Code
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 40 41 42 43 44 45 46 47 48 49 50 51 |
/** * Creates a menu item in the current document. * Google Apps Script simple trigger */ function onOpen() { DocumentApp.getUi() .createMenu("CheckBox DTS") .addItem("Add DTS", "appendElement") .addToUi(); } /** * Creates a date-time stamp from the area the cursor is on. * Called when the "Add DTS" button is selected from the menu. */ function appendElement() { // Document variables. const doc = DocumentApp.getActiveDocument(); const cursor = doc.getCursor(); const element = cursor.getElement(); // Input variables. let date = new Date(); let email = Session.getActiveUser().getEmail(); const inputVal = ` -Complete: ${date.toDateString()} ${date.toLocaleTimeString()} | ${email} `;// Add a space at the end. // Element lenght before and after adding date-time stamp. const inputValLength = inputVal.length; const elementLength = element.asText().getText().length; const newElementLength = elementLength + inputValLength - 2; // -2 to not style the last space. // Create the styling for the text. const attr = DocumentApp.Attribute; const style = { [attr.FONT_FAMILY]: "Merriweather", [attr.ITALIC]: true, [attr.BOLD]: true, [attr.BACKGROUND_COLOR]: "#eeeeee", [attr.FOREGROUND_COLOR]: "#34a853", }; // Append the text and add the styling to it. element.asText() .appendText(inputVal) .setAttributes(elementLength, newElementLength, style); }; |
Run the script
Go to Tools > Script editor to open the Google Apps Script IDE. Copy the code above and paste it into the script editor. Ctrl + s to save.
You will probably be prompted to change the title of the script. Generally, for document bound scripts it’s good practice to give the script the same or similar name as the attached document.
Close the editor and go back to your Google Doc. Refresh the page and your menu will now reload each time with your new menu item.
The first time you run the script you will get a warning to authenticate the code.
You can find out more about this here:
Running Google Apps Script for the First time. What’s with all the Warnings!
After you have run the code for the first time, you can now click the menu item and it will add the date-time stamp to where ever you have your cursor.
Need help with Google Workspace development?
Go something to solve bigger than Chat GPT?
I can help you with all of your Google Workspace development needs, from custom app development to integrations and security. I have a proven track record of success in helping businesses of all sizes get the most out of Google Workspace.
Schedule a free consultation today to discuss your needs and get started or learn more about our services here.
Code breakdown
onOpen()
1 2 3 4 5 6 7 8 9 10 |
/** * Creates a menu item in the current document. * Google Apps Script simple trigger */ function onOpen() { DocumentApp.getUi() .createMenu("CheckBox DTS") .addItem("Add DTS", "appendElement") .addToUi(); } |
The onOpen() function is a Google Apps Script custom simple trigger that is run each time the Google Doc, (Slide, Form or Sheet) is opened.
For our purposes, we want to create a menu item. Menu items are part of the Google Docs user interface and also consist of dialogue boxes, sidebars, prompts and alerts. So we can use the getUi() method from the DocumentApp Class. Line 6
To build the menu item, we first call the createMenu(‘menu title’) method that takes a title as an argument and commences the build chain to develop the menu. Line 7
We can add as many sub-menu items to our main menu by using the addItem() method. For example, if you wanted to create different displays of your date-time stamp append tool you could add more here.
Each addItem()
takes two arguments (Line 8):
- The title of the menu item: For us, this is “Add DTS”.
- The function to run when you click the menu item: In our case, this is “appendElement”.
Once we have added everything we wanted to our menu, we build it with the addToUi() method. Line 9
appendElement()
This is the main function that runs your date-time stamp appender.
Let’s see how it works.
Document variables
1 2 3 4 5 6 7 8 9 10 11 |
/** * Creates a date-time stamp from the area the cursor is on. * Called when the "Add DTS" button is selected from the menu. */ function appendElement() { // Document variables. const doc = DocumentApp.getActiveDocument(); const cursor = doc.getCursor(); const element = cursor.getElement(); ... |
Our first task is to set up our document variables.
To get the Google Doc we are using in Google Apps Script, we call the DocumentApp class and then call the getActiveDocument() method. This will create an instance of the DocumentApp class containing all the methods that you can use to work with the document. We’ll assign this instance as ‘doc’. Line 7
Referencing our ‘doc’ instance, our next job is to get where the cursor is. We can do this with the getCursor() method. Line 8
A Google Doc is broken into elements, like paragraphs, lists, tables, pictures, headers and more. Each element can have child elements as well.
Once we have our cursor position, we can find the element that the cursor is on. This will come in handy in a moment when we append the element. Line 9
Input variables
1 2 3 4 5 6 7 8 |
... // Input variables. let date = new Date(); let email = Session.getActiveUser().getEmail(); const inputVal = ` -Complete: ${date.toDateString()} ${date.toLocaleTimeString()} | ${email} `;// Add a space at the end. ... |
Our next task is to add our input variables.
This is where you can start getting really creative and change what is displayed when you click the ‘Add DTS’ menu item.
In our example, we are going to add a string of text containing:
- Today’s date.
- Today’s time.
- The current user’s email.
We first create a new date with JavaScript’s Date constructor. Line 4
Next, let’s use the Google Apps Script Session class to grab our email. We can find the active user, a.k.a. the current user, with the getActiveUser() method that then has a child method to get the email with getEmail(). Line 5
Note that all editors of your Google Doc will need to give permission for the scope that controls the Session class the first time that they click the menu button.
Now we can put our date and email together in a string. I am using template literals indicated with backticks or grave accents to store our string data because it makes it easy to insert variables into the string with the
template approach.${code}
After I indicate “Complete” at the beginning of the text string, I use the Javasript toDateString() and then toLocaleTimeString() methods on the date variable. This will provide the date and time in your local area (Or more specifically, to the date and time that you have assigned to your script). Line 6
Finally, I add my email.
You can remove any of these items that you don’t want or move them into a better order to suit your own purposes.
get element lengths
1 2 3 4 5 6 7 8 |
... // Element lenght before and after adding date-time stamp. const inputValLength = inputVal.length; const elementLength = element.asText().getText().length; const newElementLength = elementLength + inputValLength - 2; // -2 to not style the last space. ... |
To style our appended date time stamp we will need to get the length of the element and then get the length of the element plus the newly added date-time stamp. Line 4
We then get the length of the chosen element. Line 5
We will be applying our styling to the end of the old element length through to the length of the element plus the length of the input.
Note that we will subtract 2 from this value. Why? Because if we added the value right to the end of the element and a user select the end of the element and hits ‘enter’, they will bring the formatting with them. This will make the user sad. Nobody wants a sad user. Line 6
Create text styling
There are two ways of applying styling to text elements in Google Apps Script. We could simply chain a related method like setBold(), setFontFamily() or setForegroundColor() methods to your appended text. This is an okay approach for one or two styles but will slow your code down for more. There is a better approach.
The setAttributes() allows you to set a whole bunch of styles on a particular area of text and then send them to your Google Doc in one big hit.
We will be chaining the setAttributes()
method to our text append method in a moment, but first, let’s talk about how the method takes style data.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... // Create the styling for the text. const attr = DocumentApp.Attribute; const style = { [attr.FONT_FAMILY]: "Merriweather", [attr.ITALIC]: true, [attr.BOLD]: true, [attr.BACKGROUND_COLOR]: "#eeeeee", [attr.FOREGROUND_COLOR]: "#34a853", }; ... |
The last argument for setAttribues()
is a list of attributes. These attributes are formatted in an object. Each of the objects property keys needs to come from the DocumentApp.Attribute enumerator. You can find a list of properties here:
To make things easier, let’s create the attr
variable and assign it to DocumentApp.Attribute. Line 4
Next, we can build our object of attributes. Lines 5-11
Note that I have used square brackets around each key to generate the property key allowing for computed property names.
Append the text and styling
Finally, we get to append the text to the chosen element.
1 2 3 4 5 6 7 8 |
... // Append the text and add the styling to it. element.asText() .appendText(inputVal) .setAttributes(elementLength, newElementLength, style); }; |
We first grab the element and read it as a text item (Text Class). Line 4
Next, we append the inputVal
text to the end of the element with the appendText() method. Line 5
Finally, we use the setAttributes() method to add our style. This method can take:
- A start range: {number} For us, that this is the elements original length.
- The end range: {number} This will be our combined length of the original element plus our appended text minus 2.
- The style: {Object} This is the object of style properties we created in the previous section.
Conclusion
That’s all there is to it. You can modify the style and what you write in your text to how you would like it. I would love to hear how you modified your script for your own project. It is always interesting to see.
If you have found the tutorial helpful, why not shout me a coffee ☕? I'd really appreciate it.
Happy coding!
~Yagi