I have the following code that puts bold style some keywords in a whole google document:
function boldKeywords() {
// Words that will be put in bold:
var keywords = ["end", "proc", "fun"];
var document = DocumentApp.getActiveDocument();
var body = document.getBody();
var Style = {};
Style[DocumentApp.Attribute.BOLD] = true;
for (j in keywords) {
var found = body.findText(keywords[j]);
while(found != null) {
var foundText = found.getElement().asText();
var start = found.getStartOffset();
var end = found.getEndOffsetInclusive();
foundText.setAttributes(start, end, Style)
found = body.findText(keywords[j], found);
}
}
}
But I would like the code to put the keywords in bold only in the selected area of the document, for doing that, I tried using the function getSelection()
, but I have the problem that this function returns a Range
, but for applying findText
I need a Body
, somebody knows what could I do?
function boldKeywordsInSelection() {
const keywords = ["end", "proc", "fun"];
const document = DocumentApp.getActiveDocument();
const selection = document.getSelection();
// get a list of all the different range elements
const rangeElements = selection.getRangeElements();
const Style = {};
Style[DocumentApp.Attribute.BOLD] = true;
// forEach used here because for in was giving me trouble...
rangeElements.forEach(rangeElement => {
// Each range element has a corresponding element (e.g. paragraph)
const parentElement = rangeElement.getElement();
// fixing the limits of the bold operations depending on the selection
const startLimit = rangeElement.getStartOffset();
const endLimit = rangeElement.getEndOffsetInclusive();
for (j in keywords) {
let found = parentElement.findText(keywords[j]);
// wrapping in try catch to escape the for loop from within the while loop
try {
while (found != null) {
const foundText = found.getElement().asText();
const start = found.getStartOffset();
// Checking if the start of the word is after the start of the selection
if (start < startLimit) {
// If so, then skip to next word
found = parentElement.findText(keywords[j], found);
continue;
}
// Checking if the start of the word is after the end of the selection
// if so, go to next element
if (start > endLimit) throw "out of selection";
const end = found.getEndOffsetInclusive();
foundText.setAttributes(start, end, Style)
found = parentElement.findText(keywords[j], found);
}
} catch (e) {
Logger.log(e)
continue;
}
}
})
}
See the comments in the code for more details.
A getSelection
produces a Range
object, which contains within it various instances of RangeElement
. Each RangeElement
makes reference to a parent element, and the index positions within that parent. The parent is the element that the range element is a part of. For example:
This selection spans 3 elements. Therefore the selection has 3 range elements. You can only use the findText
method on the whole element, not the range element.
This means that the flow of the script is generally the same, except that you need to go through each element and find the text within each element. Since this will return elements that are outside the selection, you need to keep track of the index positions of the selection and the found element and make sure the found element is within the selection.