.getRangeElements()
возвращает массив RangeElements .Элемент range - это объект-обертка, который используется, чтобы помочь нам справиться с частичным выбором.Мы можем вызвать .getElement()
для каждого элемента в этом массиве, чтобы получить объект Element , который является очень универсальным объектом, который может представлять практически любой фрагмент Google Doc.Elements
имеет метод .getType()
, который возвращает ElementType enum;и их много !
Давайте использовать то, что нам известно, чтобы увидеть, какие возможные типы есть в Google Doc (я создал )похож на ваш (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
А-а-а! Похоже, нам нужно иметь дело только с PARAGRAPH , LIST_ITEM и TABLE ElementTypes на данный момент , но давайте также будем помнить об их дочерних элементах (мы выясним, что это 3 из 5что может иметь детей).Это похоже на работу для рекурсивной функции , которая будет постоянно копаться в дочерних элементах, пока мы не найдем и не разберемся со всеми ними.
Так что давайте попробуем это. Следующая часть может показаться запутанной, но, по сути, она находит элемент, проверяет, есть ли у него дочерние элементы, затем смотрит на них, чтобы узнать, есть ли у у них дочерние элементы, и так далее.Мы также хотим проверить, получаем ли мы 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
Хорошо, поэтому каждая строка журналацепочка стихий и их дети.У нас есть новых ElementTypes ( HORIZONTAL_RULE , TABLE_ROW и TEXT ).Если цепочка имеет значение Paragraph
и не имеет дочерних элементов, это обозначается как «ПАРАГРАФ».мы можем игнорировать его , поскольку это пустая строка.Мы также можем игнорировать HORIZONTAL_RULE
, поскольку , очевидно, не будет содержать текст.
Если мы получили элемент TEXT , это означает, что мы можем выполнять нашу функцию (т. Е. Для OP это был бы перевод), как мы это делали с LIST_ITEMs и PARAGRAPH.Тем не менее, нам все же приходится иметь дело с TableRow Объектами (которые регистрируются следующим образом: TABLE.TABLE_ROW
).Это аналогично нашим основным 3 элементам и может использоваться с нашим if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH")
, который меняется на if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH" || elemType == "TABLE_ROW")
.
Это дает нам еще один новый элемент в нашей цепочке; TableCell (журналы как: TABLE.TABLE_ROW.TABLE_CELL
), которые мы можем снова добавить к нашему заявлению if, сделав это: if(elemType == "TABLE" || elemType == "LIST_ITEM" || elemType == "PARAGRAPH" || elemType == "TABLE_ROW" || elemType == "TABLE_CELL")
Времячтобы увидеть, что происходит , когда мы имеем дело с Table ElementTypes.
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 текстового элемента , либо пустого абзаца !Отсюда мы можем немного изменить наш код, чтобы добавить функции, которые мы хотим выполнять при сохранении структуры документа:
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.
}
}
BOOM!Готово. Я знаю, что это действительно длинный пост, но я разбил каждый раздел решения на части, чтобы помочь новым кодировщикам Apps Script понять структуру Выделения (и, я полагаю, Тела документа) и какизменить его, когда структура очень сложна (много вложенных элементов). Я действительно надеюсь, что это было полезно .Если кто-нибудь увидит кусок, который можно улучшить, дайте мне знать.
В качестве примечания к OP: Имейте в виду, что это не обязательно относится к частичному выбору элемента, но этоможно легко разобраться, немного изменив первую функцию для проверки isPartial()
на RangeElement .