Google Apps 脚本:在 Google 文档中按 ID/名称插入和更新文本
我希望能够使用Google Apps脚本,具有给定ID的自定义文本插入Google文档中,以便以后能够更新它们(任何次数)。插入应与光标放置以及更换任何选定的元素一起使用。
我的代码对此非常有效(部分基于 this 答案),请参阅下面。我使用“
// function for inserting text
insertAny = (textToInsert, textName = null, range = null) => {
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
var rangeBuilder = null;
if (cursor && (range === null)) {
// Attempt to insert text at the cursor position. If the insertion returns null, the cursor's
// containing element doesn't allow insertions, so show the user an error message.
var cElement = cursor.insertText(textToInsert);
if (!cElement) {
textName = null
DocumentApp.getUi().alert('Cannot insert text here.');
} else {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(cElement);
}
} else {
var selection;
if (range === null) {
selection = DocumentApp.getActiveDocument().getSelection();
} else {
selection = range;
}
if (!selection) {
textName = null
DocumentApp.getUi().alert('Insertion omitted: A cursor placed in the text or a selected text is needed to indicate the position of the insertion.');
} else {
var elements = selection.getRangeElements();
var replace = true;
for (var i = 0; i < elements.length; i++) {
if (elements[i].isPartial()) {
var tElement = elements[i].getElement().asText();
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
var text = tElement.getText().substring(startIndex, endIndex + 1);
tElement.deleteText(startIndex, endIndex);
if (replace) {
tElement.insertText(startIndex, textToInsert);
if (rangeBuilder === null) {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(tElement, startIndex, startIndex + textToInsert.length - 1);
}
replace = false;
}
} else {
var eElement = elements[i].getElement();
// if not specified as "any", throws type errors for some reason
if (replace && eElement.editAsText) {
eElement.clear().asText().setText(textToInsert);
replace = false;
if (rangeBuilder === null) {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(eElement);
}
} else {
if (replace && i === elements.length - 1) {
var parent = eElement.getParent();
parent[parent.insertText ? 'insertText' : 'insertParagraph'](parent.getChildIndex(eElement), textToInsert);
if (rangeBuilder === null) {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(eElement);
}
replace = false; //not really necessary since it's the last one
}
eElement.removeFromParent();
}
}
}
}
}
if (textName !== null && rangeBuilder !== null) {
doc.addNamedRange(textName, rangeBuilder.build());
}
}
// function for updating text
const updateNamedRange = (textName, newText) => {
var doc = DocumentApp.getActiveDocument();
var myNamedRanges = doc.getNamedRanges(textName);
for (var i = 0; i < myNamedRanges.length; i++) {
var range = myNamedRanges[i].getRange();
insertAny(newText, textName, range);
}
}
有什么想法(或更好的解决方案)吗?
I want to be able to insert, in Google Docs using Google Apps Script, custom texts with a given ID, so that afterwards I'd be able to update them (any number of times). The insertion should work with cursor placement as well as with replacing any selected elements.
I have a code that works pretty well for this (based partly on this answer), see below. I use "named ranges" for IDing the inserted/updated texts. The only problem is, when I have several such inserted texts immediately next to each other, and I update both repeatedly, suddenly the preceding one "absorbs" the following one (i.e., deletes it). So clearly it is a problem of the named ranges somehow expanding into each other, but I cannot figure out why.
// function for inserting text
insertAny = (textToInsert, textName = null, range = null) => {
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
var rangeBuilder = null;
if (cursor && (range === null)) {
// Attempt to insert text at the cursor position. If the insertion returns null, the cursor's
// containing element doesn't allow insertions, so show the user an error message.
var cElement = cursor.insertText(textToInsert);
if (!cElement) {
textName = null
DocumentApp.getUi().alert('Cannot insert text here.');
} else {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(cElement);
}
} else {
var selection;
if (range === null) {
selection = DocumentApp.getActiveDocument().getSelection();
} else {
selection = range;
}
if (!selection) {
textName = null
DocumentApp.getUi().alert('Insertion omitted: A cursor placed in the text or a selected text is needed to indicate the position of the insertion.');
} else {
var elements = selection.getRangeElements();
var replace = true;
for (var i = 0; i < elements.length; i++) {
if (elements[i].isPartial()) {
var tElement = elements[i].getElement().asText();
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
var text = tElement.getText().substring(startIndex, endIndex + 1);
tElement.deleteText(startIndex, endIndex);
if (replace) {
tElement.insertText(startIndex, textToInsert);
if (rangeBuilder === null) {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(tElement, startIndex, startIndex + textToInsert.length - 1);
}
replace = false;
}
} else {
var eElement = elements[i].getElement();
// if not specified as "any", throws type errors for some reason
if (replace && eElement.editAsText) {
eElement.clear().asText().setText(textToInsert);
replace = false;
if (rangeBuilder === null) {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(eElement);
}
} else {
if (replace && i === elements.length - 1) {
var parent = eElement.getParent();
parent[parent.insertText ? 'insertText' : 'insertParagraph'](parent.getChildIndex(eElement), textToInsert);
if (rangeBuilder === null) {
rangeBuilder = doc.newRange();
rangeBuilder.addElement(eElement);
}
replace = false; //not really necessary since it's the last one
}
eElement.removeFromParent();
}
}
}
}
}
if (textName !== null && rangeBuilder !== null) {
doc.addNamedRange(textName, rangeBuilder.build());
}
}
// function for updating text
const updateNamedRange = (textName, newText) => {
var doc = DocumentApp.getActiveDocument();
var myNamedRanges = doc.getNamedRanges(textName);
for (var i = 0; i < myNamedRanges.length; i++) {
var range = myNamedRanges[i].getRange();
insertAny(newText, textName, range);
}
}
Any ideas (or better solutions)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好的,似乎原因是,如果我立即插入命名范围旁边的文本,它将自动属于该范围。 (因此,随后的更新也影响了这些无关的部分。)
我真正的技巧解决方案是暂时插入占位符角色,以将新文本与任何名称范围的潜在范围区分开……这让我发笑,但我尝试过的其他任何事情也没有工作。对于我能想到的所有棘手场景,这似乎是强大的。我的最终代码在下面。
Okay, so it seems that the reason is that if I insert text immediately next to a named range, it will automatically belong to that range. (Hence subsequent updates affected these unrelated parts too.)
My really hacky solution is to temporarily insert a placeholder character to separate the new text from any potential named ranges... It makes me laugh, but nothing else I tried works as well. This seems to be robust to all the tricky scenarios I can think of. My final code is below.