Retrieving a Google Docs body text is quite easy with the help of Google Apps Script.
Well, until it isn’t. Let me explain.
Continue reading “Get a Google Docs Body Text with Apps Script”
Google Workspace and Software Development
Retrieving a Google Docs body text is quite easy with the help of Google Apps Script.
Well, until it isn’t. Let me explain.
Continue reading “Get a Google Docs Body Text with Apps Script”
If you’ve landed on this page you’re probably wondering why your hyperlinks are not working in your Google Workspace dialogue (dialog for my U.S. friends) box or sidebar.
This affects all locations where you can build a sidebar or dialogue with Google Apps Scripts, Sheets, Docs, Slides and Forms.
You might even hit F12 in your browser to inspect the code and found this dreaded error:
Unsafe attempt to initiate navigation for frame with origin ‘https://docs.google.com’ from frame with URL ‘https://n-yyi3lctp…<<fileID>>…-0lu-script.googleusercontent.com/userCodeAppPanel’. The frame attempting navigation of the top-level window is sandboxed, but the flag of ‘allow-top-navigation’ or ‘allow-top-navigation-by-user-activation’ is not set.
So what’s going on?
Dialogues and sidebars in Google Workspace are set in iframes. Essentially, this is a nested webpage on your main page. Take a look at the examples, below. I’m in Chrome here and I have selected the Developer Tools Element Selector (Ctrl + Shift + C for PC) and clicked on the Sidebar and Dialogue box respectively.
For the Sidebar.
And for the dialogue box.
When you create a simple HTML hyperlink in your anchor tag like this:
<a href="https://yagisanatode.com">Website</a>
The solution is really easy. Simply add target="_blank"
to your anchor element:
<a href="https://yagisanatode.com" target="_blank">Website</a>
rel="noopener"
behaviour on most browsers to prevent the destination link from tampering with the original source.If you have found the tutorial helpful, why not shout me a coffee ☕? I'd really appreciate it.
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.
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.
So you are a citizen Google Apps Script developer and you’ve decided to make yourself a mail-merge-type project where you want to create new documents from a template. You have discovered the simplicity of the replaceText() method:
1 2 3 |
var body = DocumentApp.getActiveDocument().getBody(); body.replaceText("{{TEMPLATE TEXT}}", "My New Text"); |
Now you want to take it to the next level and replace the text with a hyperlink containing the text and the URL. You might be scratching your head wondering where the replaceTextWithLink()
method is or why you can’t simply chain the setLinkUrl()
method without making a hyperlink out of the entire body of the document.
What to do?
In this tutorial, I’ll cover how to find and replace text in a Google Doc with a hyperlink with Google Apps Script under three common conditions:
Danger!!! Word repetition warning ahead!
I encourage you to play along. Here is a link to the Google Doc without the code attached:
Just go to File > Make a copy to get your own copy of the Google Doc. Then Tools > Script Editor.
While you are testing, you can just use undo (ctrl + z) to return the text to its original state.
Let’s dive into the three examples.
Table of Contents
In our first example, we have a paragraph where we have just the text that we want to replace. Take a look at the image:
Here’s the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/** * Find an replace text with a single link where there is no * other text in the paragraph. */ function singleLink() { // ## Inputs ## let text = "My URL"; let url = "https://yagisanatode.com/"; let textToFind = "{{SINGLE LINK}}"; // ############ let body = DocumentApp.getActiveDocument().getBody(); body.findText(textToFind) .getElement() //Gets the current text element. .asText() //Gets the element as a Text item. .setText(text) //Updates the text for that element. .setLinkUrl(url); //Sets the hyperlink for that element. }; |
Once we have grabbed our body element on line 12, we set up our chain of methods to produce our hyperlink.
First, we use the findText() method to grab the text we want to find in the body. This method takes our textToFind
variable as an argument and returns a range element indicating the position of the searched text. Line 14
Next, we get the element that the found range of text is in using the getElement() method. This will be a text element. Line 15
Note! You can find the type of text element by using this approach:
1 |
console.log(body.findText(textToFind).getElement().getType().name()) |
We then call the asText() method to get the current element as … well … um … text so that we can edit it. This allows us to perform rich text editing of the element. Line 16
Now we can set the text we want to use to replace the current text with setText(), inputting our text
variable. Line 17
Finally, we add our link using setLinkUrl(). This will take our url
variable as its argument. Line 18
Note that this approach will replace all the text associated with the element removing your reference search text and any other text. If you want to replace the selected text in a paragraph and add a link to it, check out the next example.
In this example, we only want to replace the target text (and add a link) that resides inside a paragraph. Here is our example:
We need to do three things here.
Check out 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 |
/** * Find and replace text with a single link where there is more text * in the paragraph. */ function singleLinkWithinParagraph(){ // ## Inputs ## let text = "My URL"; let url = "https://yagisanatode.com/"; let textToFind = "{{SINGLE LINK IN PARA}}"; // ############ let body = DocumentApp.getActiveDocument().getBody(); let foundText = body.findText(textToFind); // Get the start and end location of the text in the paragraph. let startText = foundText.getStartOffset(); let endText = startText + text.length - 1; // Get the element indext for this section of text. let element = foundText.getElement(); // Replace the text and insert the URL. element.asText() .replaceText(textToFind, text) .setLinkUrl(startText, endText, url); }; |
Again we start off by grabbing our body on line 12. We won’t be able to chain our methods too much here because we need to get some extra information out of them. So instead we set foundText
to the result of our findText()
method call. Line 14
Our next task is to get the start and end locations of the text within the greater text. We can get the start location (or offset) by using the getStartOffset() method. This essentially gets how many characters in our text starts on. Line 17
We then need the location where our text will end. Now, this is not the end location of the current text. It is the location of the text that we are going to use to replace it. To calculate this, we add the startText
to the length of our replacement text. We need to subtract one because the startText
value is the beginning location of our text and not the character location previous. Line 18
Now we can get cracking and replace our text.
First, we grab the element (text) of our foundText
. Line 21
We can then chain our next steps by setting the element to text. Line 24
This time around we can use the beloved replaceText()
method to find the text again only searching inside the text element and replacing it with our desired text. Line 25
From here we can now set our link. This time around we will take advantage of setLinkUrl()
method’s alternate parameter arrangement which takes:
startText
endText
url
This allows us to set the link at a specific location in the text.
But what if you want to add multiple hyperlinks to a list, Yagi?
In this final example, we want to add a list of links based on a text reference in the document.
Take a look at the document.
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 |
/** * Find text and replace it with a list of links. */ function multiLinkSet(){ // ## Inputs ## const links = [ { title: "My website", url: "https://yagisanatode.com/" }, { title: "Twitter", url: "https://twitter.com/LifeOfSpy/" }, { title: "Facebook", url: "https://www.facebook.com/yagisanatode" } ]; let textToFind = "{{LINKS}}"; // ############ let body = DocumentApp.getActiveDocument().getBody(); // Gets the paragraph element containing the text. let element = body.findText(textToFind) .getElement() .getParent(); // Gets the index location of the para containing the text. let index = body.getChildIndex(element); // Removes the paragraph element from the text. element.removeFromParent(); //Loop through the list of link objects and add to the document. links.slice().reverse().forEach(link =>{ body.insertListItem(index , link.title) .setLinkUrl(link.url) .setGlyphType(DocumentApp.GlyphType.NUMBER) }); }; |
In this example, we have an array of objects containing the title and URL for each of the links we want to add within our links
variable. Lines 6 – 19
Just like in the previous two examples, you could get your data from many other sources. This is just an easy example of data to follow.
Before I explain this step, it is important to know that our sample text resides inside a text element that resides inside a paragraph element which will probably reside inside the body element.
Our ultimate goal is to remove the selected text and replace it with a list. If we just remove the text element, we will still be left with the paragraph, which will look like a carriage return (do kids still use that term?). So we will want to remove that whole paragraph.
This means that our first step is to get the paragraph element that contains our text.
We do this first by finding the text (Line 27). We grab the text element (Line 28). This allows us to get the element’s parent with the getParent()
method. This is stored in our element
variable.
Here on line 32, we grab the index location of our template text. We head back to the body
for this one and use the getChildIndex() on our paragraph element of the selected text. This method returns an index of the location in the body
element.
The index will allow us to add our list of links in a moment.
Now that we have the index location of where we need to add our list of links, we can safely remove our reference text.
To do this, we grab the paragraph element
and use the removeFromParent()
method. Line 35
Our final step is to push our list into our Google Doc at our new index location.
The text will be inserted into the new index location. This means that if we looped through our text and inserted it at the same index each time, the links will appear in the opposite order that we originally had them in our array.
The first step then is to get a reversed copy of the array before we start our loop (We get a copy because we don’t want to change the original array). This is achieved with the Javascript slice() method without any parameters, which collects the whole array. Then we use the reverse() Javascript method on it to reverse the order of the array. Now we have a copy of the array in reverse order, but we haven’t change the original array. Line 37
Now we can run our foreach() loop to iterate through each array item.
Inside each iteration of our loop, we want to use the insertListItem() method to add our list item to the index location of our Google Doc body
(Line 39). This method takes two arguments:
index
link.text
The method then returns the newly created list item element.
Here we can then add our link using setLinkUrl().
Before we finish with our list item we can set the type of list we want by using the setGlyphType() method. The method takes a ‘list character type’ which is drawn from the Glyph Type enumerator. For our example, we set our list to be numbered.
Give it a crack yourself!
So that’s it. Three different scenarios for you to insert hyperlinks based on a text key in Google Docs with Google Apps Script. Of course, there is more than one way to do things. I would love to hear your approach to these problems in the comments below.
I’d also love to hear how you used these scripts in your own project. It is always inspirational.
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.
~Yagi
Google Apps Script: SpreasheetApp, DocumentApp, DriveApp; Google Sheets, Google Docs
If you have ever worked in LibreOffice or Microsoft Excel you will probably be familiar with the mail merge. Traditionally, mail merge is used to create multiple versions of a document and snail-mail them to someone.
These days, we don’t often use the snail mail approach, but it is a regular occurrence for us to need to produce multiple versions of reports based on a data set usually from a spreadsheet.
In this tutorial, we will create a document merger that will create new Google Documents based on a dataset from a Google Sheet using Google Apps Script.
If you want to quickly jump into your own project with our script, I’ll provide you with a quick-use guide.
Then, we will set up a template for our Google Doc and generate our Google Sheet data (don’t worry, I’ll share the document so you can follow along).
Finally, we will jump into the breakdown of the code for those legends who are learning how to create their own Google Apps Script.
Let’s get started:
Note: As always, take what you need and don’t worry about the rest.