1

I'm trying to iterate over every element in the document, acquiring the start/end positions of each paragraph. But it keeps return -1. Any ideas what I'm doing wrong?

  var body = doc.getBody(); 

  var elements = body.getNumChildren();
  for( var i=0;i<elements;i++) {

   var element = body.getChild(i).copy();            
   var type = element.getType();
   if( type == DocumentApp.ElementType.PARAGRAPH ){
     var text = element.asParagraph().getText();

     var range = element.asParagraph().findElement(DocumentApp.ElementType.PARAGRAPH);
     var start = range.getStartOffset();
     var finish = range.getEndOffsetInclusive();
Rubén
  • 34,714
  • 9
  • 70
  • 166
Paul Brown
  • 223
  • 1
  • 14
  • What are you logging? Where are you defining the variable `doc`? – Brian Aug 29 '18 at 13:56
  • Whilst developing this single method I'm using this: var doc = DocumentApp.openById('1Q6v8ipqA81LoPtpk71NdqTaIEqMjki1KIJbrm0bILBg'); var body = doc.getBody(); Once I have it working I'll integrate the method into my application like this: var body = DocumentApp.getActiveDocument().getBody(); – Paul Brown Aug 29 '18 at 14:14
  • There is no such thing as "the start/end positions of each paragraph" in a Google Document, and therefore there is no way to find them. `getStartOffset` etc refer to something else. Google Document structure is similar to HTML. What is the starting position of a `div` element on a webpage? –  Aug 29 '18 at 23:22

1 Answers1

0

According to the official docs, the Range class is only accessible if there is a selection by the current user on the page. So, you can get the script to work, but it requires some user input rather than running on a blank document.

function getRange() {
  var ui = DocumentApp.getUi();

  // Get the user selection. Display an alert if there is none.
  var selection = DocumentApp.getActiveDocument().getSelection();

  if(selection) {

  // Get all the elements in the current selection
  var elements = selection.getRangeElements();

  for( var i=0;i<elements.length;i++) {

   var el = elements[i]

   // Can it be edited as text? If not, skip it
   if(el.getElement().editAsText) {
     var start = el.getStartOffset()
     var finish = el.getEndOffsetInclusive();
     ui.alert("start: " + start + ", finish: " + finish)
   } else {
      ui.alert("Text not selected!");
    }
  }
  } else {
    ui.alert("Nothing is selected!");
  }
}

The offsets check the position of the selection against the last element. If it is text, then it will return the index. If not (such as a page break or image) it will return -1.

Adding a simple onOpen event will allow you to run the script from a custom menu for testing:

function onOpen() {
  var ui = DocumentApp.getUi();

  ui.createMenu("Offsets").addItem("Run", "getRange").addToUi()
}
Brian
  • 4,274
  • 2
  • 27
  • 55
  • That is a bizarre restriction. Forcing the end-user to select the entire document prior to clicking a button is not appropriate for my use-case. Is there no way of getting all the ranges in the document? I'm trying to implement the answer to this other question, but I need the implementation to work on the whole document, not just a selected area: https://stackoverflow.com/questions/37771381/eliminate-newlines-in-google-app-script-using-regex – Paul Brown Aug 29 '18 at 15:14
  • No, because a range _is_ the selection. What's the end purpose? Why can't you get all paragraphs in the document in an array and then loop? – Brian Aug 29 '18 at 15:45
  • I'm trying to replace all the \n characters for \v characters, given that there are issues with regex recognising \n... as detailed in the article referenced in my previous comment – Paul Brown Aug 29 '18 at 16:08