Google Apps 脚本:在 Google 文档中按 ID/名称插入和更新文本

发布于 2025-01-20 01:52:46 字数 4190 浏览 1 评论 0原文

我希望能够使用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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

段念尘 2025-01-27 01:52:46

好的,似乎原因是,如果我立即插入命名范围旁边的文本,它将自动属于该范围。 (因此,随后的更新也影响了这些无关的部分。)

我真正的技巧解决方案是暂时插入占位符角色,以将新文本与任何名称范围的潜在范围区分开……这让我发笑,但我尝试过的其他任何事情也没有工作。对于我能想到的所有棘手场景,这似乎是强大的。我的最终代码在下面。

 const 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();
            if (range !== null) {
                elements.length = 1;
            }
            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);
                    if (replace) {
                        tElement.insertText(endIndex + 1, 'x');
                        tElement.deleteText(startIndex, endIndex);
                        tElement.insertText(startIndex + 1, textToInsert);
                        if (rangeBuilder === null) {
                            rangeBuilder = doc.newRange();
                            rangeBuilder.addElement(tElement, startIndex + 1, startIndex + 1 + textToInsert.length - 1);
                        }
                        replace = false;
                        tElement.deleteText(startIndex, startIndex);
                    } else {
                        tElement.deleteText(startIndex, endIndex);
                    }
                } 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());
    }
}


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();
        myNamedRanges[i].remove();
        insertAny(newText, textName, range);
    }
}

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.

 const 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();
            if (range !== null) {
                elements.length = 1;
            }
            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);
                    if (replace) {
                        tElement.insertText(endIndex + 1, 'x');
                        tElement.deleteText(startIndex, endIndex);
                        tElement.insertText(startIndex + 1, textToInsert);
                        if (rangeBuilder === null) {
                            rangeBuilder = doc.newRange();
                            rangeBuilder.addElement(tElement, startIndex + 1, startIndex + 1 + textToInsert.length - 1);
                        }
                        replace = false;
                        tElement.deleteText(startIndex, startIndex);
                    } else {
                        tElement.deleteText(startIndex, endIndex);
                    }
                } 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());
    }
}


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();
        myNamedRanges[i].remove();
        insertAny(newText, textName, range);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文