Can you set rowspan / colspan through apps script in Google document?


Does anyone have an example of how/if we can set the row/colspan on a TableCell (?) Element in an Apps Script generated Google Document?

Does this need to be done using the .setAttributes() method?


No, there is currently no way to do this. I don't see a feature request on the Issue Tracker, either.

There is a .merge() method on the TableCell object, which sounds promising. However, when it's used it combines the "current" TableCell object with the "previous" sibling TableCell by appending the content of the "current" TableCell to the "previous" one, then deleting the "current" one.


<img alt="Before" class="b-lazy" data-src="https://i.stack.imgur.com/Hvfu2.png" data-original="https://i.stack.imgur.com/Hvfu2.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />


<img alt="After" class="b-lazy" data-src="https://i.stack.imgur.com/TYSZc.png" data-original="https://i.stack.imgur.com/TYSZc.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />


I modified the code from <a href="https://stackoverflow.com/a/14000073/1677912" rel="nofollow">a previous answer</a> to experiment with .merge(), here it is:

function mergeExperiment() { var folder = "StackOverflow"; var docname = "Experiment.gdoc"; var docId = getFileByName_(folder, docname).getId(); var doc = DocumentApp.openById(docId); var docBody = doc.getActiveSection(); var totalElements = doc.getNumChildren(); var el=[] for( var j = 0; j < totalElements; ++j ) { var element = doc.getChild(j); var type = element.getType(); switch (type) { case DocumentApp.ElementType.PARAGRAPH: break; case DocumentApp.ElementType.TABLE: var tablerows=element.getNumRows(); for ( var row = 0; row < tablerows; ++row ) { var tablerow = element.getRow(row) for ( var cell=0; cell < tablerow.getNumCells(); ++cell) { // Experiment - merge two cells in the second row if (row==1 && cell==1) { tablerow.getChild(cell).merge(); } } } break; } } }


I have discovered a very kludgy workaround for the lack of colspan in GAS...

<ol><li>create a table (FlexTable, Grid, etc.) with the columns/row you want</li> <li>when you get to an area where you'd like to use colspan or rowspan, create a new table (with different cell widths and heights</li> <li>keep on creating a new table every time you want to change the cell widths or heights</li> <li>create a VerticalPanel and add each of the tables you created in steps 1-3 in order</li> </ol>

You now have the <em>appearance</em> of a single table with colspan and rowspan features.

Not very elegant, but it's the only solution I've been able to come up with.


i found this question, because i wanted to get rid of a RowSpan within a table and searched for an answer to the question "How can i set RowSpan or ColSpan to 1.

As you lot did, i had to realize that there is no way to get writable access to these hidden properties of a TableCell.

But on my research i found an answer to this question. It's an uncomfortable workaround, but once when it is set up properly, it works very nice:

The key is that you can delete a cell and replace it by a deep copy of a cell, which you spanned manually before.

If you have a table with some spanning cells, created manually, for example a table with 3 rows and 3 cells, which you spanned them all, then you can make a deep detached copy of the spanning cell and insert it anywhere in another table. The inserted cell in the destination table will span the surrounding cells as it does in the source table.

You have to know that the contents of the surrounding cells are still there, but cannot be seen. And if you delete the spanning cell again or you do the unspanning manually, they will be shown again.

This insert of the already spanning srcCell has the same effect as if you would lay a 3x3 cells overlapping white paper on the destination table. All the cells are still there, but you can't see them. The guugel-doc engine shows only the contents of the spanning cell.

And this behavior can be copied. Isn't that nice?

I know, this is a moloch, if you have to span many cells with many different spanning-widths and directions, but if you designed it for your needs, it maybe worth the time you have to spend for the spanning cell patterns.

One could create a document, which holds only tables with all the spanning cells, you need. And you can use this document as your "Spanning Cells Source".

You can make a copy of the following test-document, which contains 3 tables and clearly shows the principle behind it. The first table holds a 3x3 spanning cell at tableindex 0,0. The second table holds the indexes of each cell in the cell itself as row,col pairs and a "TestCell", which should span 3x3 other cells. The 3rd table is for comparison only:

<a href="https://docs.google.com/document/d/1G8C2JP_4689RFmtxHZ2djslwoGWwHwieHfbIwr2XzeQ/edit" rel="nofollow">https://docs.google.com/document/d/1G8C2JP_4689RFmtxHZ2djslwoGWwHwieHfbIwr2XzeQ/edit</a>

And then you have access to the bound script, which preserves the contents of the "TestCell", replaces it by a 3 times 3 spanning cell and puts the content back again. You will see the following bound script:

var doc = DocumentApp.getActiveDocument(); var docBody = doc.getBody(); function myFunction() { var tables = docBody.getTables(); var srcTable = tables[ 0 ]; var dstTable = tables[ 1 ]; var foundText = docBody.findText( "TestCell" ); if ( foundText ) { // The cell that should span others var dstCell = foundText.getElement().getParent().getParent(); // If you want to preserve the contents of the cell var cellContents = []; var numChildren = dstCell.getNumChildren(); for ( var i = 0; i<numChildren; i++ ) { cellContents.push( dstCell.getChild( i ).copy() ); } // Row- and ColumnIndex of the future spanning cell var dstRowIndex = dstTable.getChildIndex( dstCell.getParent() ); var dstColIndex = dstCell.getParent().getChildIndex( dstCell ); // Deep, detached copy of an already spanning cell var srcCell = srcTable.getCell( 0, 0 ).copy(); // delete dstCell and insert the deep, detached copy of the spanning cell dstCell.removeFromParent(); dstCell = dstTable.getChild( dstRowIndex ).insertTableCell( dstColIndex, srcCell ); // If you preserved the contents of the deleted, non-spanning cell, // you can put them back again for ( var i = numChildren-1; i>=0; i-- ) { var childType = cellContents[ i ].getType(); // Do not forget to extend this switch by your other ElementTypes !!! switch ( childType ) { case DocumentApp.ElementType.PARAGRAPH: dstCell.insertParagraph( 0, cellContents[ i ] ); break; } } // Get rid of the default single and empty PARAGRAPH of the srcCell, // if you had to put back the contents of the delted non-spanning cell dstCell.removeChild( dstCell.getChild( dstCell.getNumChildren() - 1 ) ); } }

I hope, this can help.

Thank you very much for your attention,



  • Mixing TypeScript and Meteor - Classes across multiple files
  • dplyr idiom for “select A, B, max(C) from D group by C”
  • Is it possible to compile Typescript files locally but not on the build server?
  • When is it better for an assembler to use sign extended relocation like R_X86_64_32S instead of zero
  • ARM MOV and MVN operand
  • Accesing properties in a UserControl from the MainWindow (WPF/MVVM)
  • How do you copy a MS SQL 2000 database programmatically using C#?
  • Is there a method to list all objects such as VIEWs that use a SYNONYM?
  • Scala Slick Database Views
  • Is it possible to access raw iphone audio output?
  • After Installing PMD in Luna in Prespective it is Not Showing?
  • removing the default blue color on focus
  • Complex Silverlight TreeView, is nested hierarchy possible?
  • Symfony2 plaintext users don't work
  • No rows to manipulate in html table created with jQuery csvToTable?
  • Use neo4j server instead of embedded mode
  • How do you remove the JComboBox 'click and see dropdown' functionality?
  • Is there any way to call saveCurrentTurnWithMatchData without sending a push notification?
  • Avoid registering duplicate broadcast receivers in Android
  • Excel VBA How to populate a multi-dimensional (3d) array with values from multiple excel ranges?
  • ZipList with Scalaz
  • Implement JwtBearer Authentication in NSwag SwaggerUi
  • Cannot get text from text area
  • Primefaces :radioButton inside a ui:repeat
  • R convert summary result (statistics with all dataframe columns) into dataframe
  • Validate child input components on submit with Vee-Validate and vue js 2
  • AndEngine Applying Transparancy to AndEngine View
  • Breaking out column by groups in Pandas
  • Unable to get column index with table.getColumn method using custom table Model
  • Does it make sense to call System.gc() and Thread.sleep() when working on Bitmaps?
  • azure media services - The request body is too large and exceeds the maximum permissible limit
  • Projection media query: browser support and workarounds?
  • Rearranging Cells in UITableView Bug & Saving Changes
  • Circular dependency while pushing http interceptor
  • Comma separated Values
  • Linker errors when using intrinsic function via function pointer
  • Error creating VM instance in Google Compute Engine
  • Hits per day in Google Big Query
  • how does django model after text[] in postgresql [duplicate]
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET