.getRangeElements()
返回一个数组范围元素。范围元素是一个包装对象,用于帮助我们处理部分选择。我们可以打电话.getElement()
在此数组中的每个项目上获取元素对象这是一个very通用对象,可以代表 Google 文档的几乎任何部分。Elements
have a .getType()
方法返回一个元素类型枚举;并且有a lot他们!
让我们用目前所知的知识来看看 Google 文档中可能的类型(我已经创建了一个与您相似的(img)举个例子):
function selectionHasWhichTypes() {
var doc = DocumentApp.getActiveDocument();
var selection = doc.getSelection();
var rangeElems = selection.getRangeElements();
rangeElems.forEach(function(elem){
var elem = elem.getElement();
Logger.log(elem.getType());
});
}
//Logger OUTPUT:
PARAGRAPH
PARAGRAPH
PARAGRAPH
PARAGRAPH
PARAGRAPH
LIST_ITEM
LIST_ITEM
LIST_ITEM
PARAGRAPH
PARAGRAPH
PARAGRAPH
TABLE
PARAGRAPH
Ah Ha!看起来我们只需要处理段落, 项目清单, and TABLE元素类型for now,但让我们保留他们的children也记住(我们会发现这些是 5 个中的 3 个可以生孩子)。这听起来像是一份工作递归函数它将不断深入挖掘子元素,直到我们找到并处理所有子元素。
那么让我们尝试一下吧。下一部分可能看起来令人困惑,但本质上它是查找一个元素,检查它是否有子元素,然后查看它们是否有they有孩子等等。我们also想检查一下我们是否得到了new还要处理的 ElementTypes...
function selectionHasWhichTypes() {
var doc = DocumentApp.getActiveDocument();
var selection = doc.getSelection();
var rangeElems = selection.getRangeElements();
rangeElems.forEach(function(elem){
var elem = elem.getElement();
elemsHaveWhatChildElems(elem, elem.getType());
});
}
function elemsHaveWhatChildElems(elem, typeChain){
var elemType = elem.getType();
if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH"){ //Lets see if element is one of our basic 3. If so they could have children.
var numChildren = elem.getNumChildren(); //How many children are there?
if(numChildren > 0){
for(var i = 0; i < numChildren; i++){ //Let's go through them.
var child = elem.getChild(i);
elemsHaveWhatChildElems(child, typeChain + "." + child.getType()); //Recursion step to look for more children.
}
}else{
Logger.log(typeChain); //Let's log the chain of Parent to Child elements.
}
}else{
Logger.log("*" + typeChain); //Let's mark the new elemTypeChains we have not seen.
}
}
//Logger OUTPUT:
*PARAGRAPH.TEXT
PARAGRAPH
*PARAGRAPH.HORIZONTAL_RULE
PARAGRAPH
*PARAGRAPH.TEXT
*LIST_ITEM.TEXT
*LIST_ITEM.TEXT
*LIST_ITEM.TEXT
PARAGRAPH
*PARAGRAPH.TEXT
PARAGRAPH
*TABLE.TABLE_ROW
*TABLE.TABLE_ROW
PARAGRAPH
好吧,日志的每一行都是一系列元素及其子元素。我们有一些新元素类型 (水平规则, 表行, and TEXT)。如果一条链只是一个Paragraph
并且没有孩子,如“段落”所示。我们可以忽略它因为它是一个空行。我们也可以忽略HORIZONTAL_RULE
作为这个明显地不会包含文本。
如果我们已经到达一个 TEXT 元素这意味着我们可以执行我们的功能(即对于 OP 来说,这将是一个翻译),就像我们对 LIST_ITEM 和 PARAGRAPH 所做的那样。然而,我们still必须处理TableRow对象(记录如下:TABLE.TABLE_ROW
)。这是类似于我们的主要 3 个要素并可以与我们的if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH")
这会改变为if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH" || elemType == "TABLE_ROW")
.
这为我们的链中提供了另一个新元素;表格单元格(日志如下:TABLE.TABLE_ROW.TABLE_CELL
),我们可以again添加到我们的 if 语句中,使其:if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH" || elemType == "TABLE_ROW" || elemType == "TABLE_CELL")
是时候看看会发生什么了当我们处理完表元素类型时。
function selectionHasWhichtypeChains() {
var doc = DocumentApp.getActiveDocument();
var selection = doc.getSelection();
var rangeElems = selection.getRangeElements();
rangeElems.forEach(function(elem){
var elem = elem.getElement();
elemsHaveWhatChildElems(elem, elem.getType());
});
}
function elemsHaveWhatChildElems(elem, typeChain){
var elemType = elem.getType();
if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH" || elemType == "TABLE_ROW" || elemType == "TABLE_CELL"){ //Lets see if element is one of our basic 5 if so they could have children.
var numChildren = elem.getNumChildren(); //How many children are there?
if(numChildren > 0){
for(var i = 0; i < numChildren; i++){ //Let's go through them.
var child = elem.getChild(i);
elemsHaveWhatChildElems(child, typeChain + "." + child.getType()); //Recursion step to look for more children.
}
}else{
Logger.log(typeChain); //Let's log the chain of Parent to Child elements.
}
}else{
Logger.log("*" + typeChain); //Let's mark the new elemTypeChains we have not seen.
}
}
//Logger OUTPUT:
*PARAGRAPH.TEXT
PARAGRAPH
*PARAGRAPH.HORIZONTAL_RULE
PARAGRAPH
*PARAGRAPH.TEXT
*LIST_ITEM.TEXT
*LIST_ITEM.TEXT
*LIST_ITEM.TEXT
PARAGRAPH
*PARAGRAPH.TEXT
PARAGRAPH
*TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
*TABLE.TABLE_ROW.TABLE_CELL.TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
*TABLE.TABLE_ROW.TABLE_CELL.TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
*TABLE.TABLE_ROW.TABLE_CELL.TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
*TABLE.TABLE_ROW.TABLE_CELL.TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH
*TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
*TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.HORIZONTAL_RULE
*TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
*TABLE.TABLE_ROW.TABLE_CELL.PARAGRAPH.TEXT
PARAGRAPH
这很棒!我们已经深入到每个父元素并达到了a 文本元素 or a 空白段落!从这里我们可以稍微修改我们的代码以添加我们想要执行的功能,同时保持文档的结构:
function myFunction() {
var doc = DocumentApp.getActiveDocument();
var selection = doc.getSelection();
var rangeElems = selection.getRangeElements(); //Get main Elements of selection
rangeElems.forEach(function(elem){ //Let's rn through each to find ALL of their children.
var elem = elem.getElement(); //We have an ElementType. Let's get the full element.
getNestedTextElements(elem, elem.getType()); //Time to go down the rabbit hole.
});
}
function getNestedTextElements(elem, typeChain){
var elemType = elem.getType();
if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH" || elemType == "TABLE_ROW" || elemType == "TABLE_CELL"){ //Lets see if element is one of our basic 5, if so they could have children.
var numChildren = elem.getNumChildren(); //How many children are there?
if(numChildren > 0){
for(var i = 0; i < numChildren; i++){ //Let's go through them.
var child = elem.getChild(i);
getNestedTextElements(child, typeChain + "." + child.getType()); //Recursion step to look for more children.
}
}
}else if(elemType == "TEXT"){
//THIS IS WHERE WE CAN PERFORM OUR OPERATIONS ON THE TEXT ELEMENT
var text = elem.getText();
}else{
Logger.log("*" + typeChain); //Let's log the new elem we dont deal with now - for future proofing.
}
}
繁荣!完毕。我知道这是一篇非常长的文章,但我已将解决方案的每个部分分解为多个部分,以帮助新的 Apps 脚本编码人员了解选择的结构(我猜还有文档正文)以及如何在结构发生变化时修改它非常复杂(许多嵌套元素)。我真的希望这有帮助。如果有人看到可以改进的部分,请告诉我。
作为OP的注释:请注意,这不一定处理元素的部分选择,但是可以通过稍微修改第一个函数来检查来轻松处理isPartial()
on the 范围元素.