11 Replies Latest reply on Sep 16, 2013 5:46 AM by Marc Autret

    Select textframe on basis on textFrame.id


      Dear All,


      I want to assign a variable myFrame to one of the textFrame present in a page by textFrame.id.


      var myDoc = app.documents.item(0);

      var i;


      for(i=0; i < myDoc.textFrames.length; i++)     // three text frames i have in myDoc (only one page)


                var myFrame = myDoc.textFrame[i]

                alert(i + " " + myFrame.id);   // here I get number like 845, 869, 205

                if (myFrame.id == 845)







      So now I knew three id of three textframes so how i can assign/select that frame to a variable, which has id 869.


      myFrame1 = myDoc.textFrames.id(869);  // assign does not work here //need code for that





        • 1. Re: Select textframe on basis on textFrame.id
          virender_CTS Level 1

          I can do like this:


          myFrame1 = myDoc.pageItems.itemByID(869);   // but i am using pageItem function not textFrame


          • 2. Re: Select textframe on basis on textFrame.id
            S Hopkins Adobe Community Professional

            Here is another solution:

            var myItemId = 869;

            myTest = tryIt(myItemId)

            myTest.id; //should return same value as myItemId



            function tryIt (myItemId) {

               var docRef = app.documents.item(0);

               var frameCount = docRef.textFrames.count();

               var frameVar;

               for (var i=0; i < frameCount; i++){

                  if (docRef.textFrames.item(i).id == myItemId){

                     return docRef.textFrames.item(i);





            Putting the code inside a function allows the result to be returned when the match is made, eliminating any unnecessary iterations.

            • 3. Re: Select textframe on basis on textFrame.id
              DaveSofTypefi Level 2

              itemByID() is a strange call because it will return almost any object that has the id you feed it.


              For example, I created a new document and dragged out a text frame then ran this script:


              var myID = app.documents[0].textFrames[0].id
              var myObj = app.documents[0].pages.itemByID(myID);
              var whatIReallyGot = myObj.getElements()[0];


              And this gave:





              So, because I asked for a page, it assumed that I knew what I was doing and had provided a page id, but I hadn't. getElements revealed what I really got.


              But this process is not completely flexible. For example, I put a table into the text frame and tried this script:



              var myID = app.documents[0].stories[0].tables[0].id
              var myObj = app.documents[0].pages.itemByID(myID);
              var whatIReallyGot = myObj.getElements()[0];


              And this failed on the attempt to getElements because myObj was invalid.


              I think the rule is that you can do this kind of transmogrification provided that the base object (in this case, the document) can be a legitimate parent of both what you ask for and what you get.



              • 4. Re: Select textframe on basis on textFrame.id
                Laubender Adobe Community Professional & MVP

                @Dave – ok. Let's sort this out:


                Your code does not work, because:

                1. In the second line your var is not a pageItem, but a page object

                2. A Table object cannot be called as a pageItem


                You assume that all objects on a page are page items.

                Or you assume that all objects with IDs are page items.

                You assume wrong.



                The count of the IDs in a blank document with a new, single object on a page never starts with the value 0 !

                There are all sorts of things that already have a unique ID, even if there is only a single page in a fresh document.


                Let's do a little experiment and watch for the number values of certain objects:


                1. Create a new document with 1 page and add a rectangle on page 1.


                The value of the ID of that rectangle will be a number above 100.

                Let's assume the number is 199 (a real world case).


                That value could be different, e.g. if I'm using more pages at the start, if I have more colors loaded than before, etc. etc.


                2. Remove that rectangle and add a new fresh text frame to the page.


                Check the value of its ID.
                What do you assume what that number is?

                199 again ?

                200 ?


                That are fair assumptions.


                But wrong.

                The value of the ID of that text frame in my case was 224.
                What happened? The ID values from 200 to 223 are assigned to other things that are not "usual" page items such as rectangles, text frames etc. etc.


                3. Let's go on. Add a new table to that fresh text frame.

                Check its ID.


                In my case the ID was 229.


                What happened to the ID numbers 225 to 228?
                Assigned for other things.


                So we can fairly conclude, that the counting of the numbers will always go up.

                Whatever we'll do. Adding or removing pageItems, adding or removing tables in stories (or tables in cells).


                Table objects are Table objects.

                Their ID values are counted in the same scope as an ordinary page item such as a Rectangle, a Group etc.etc.
                But other things are counted in the same scope, that definitely are no geometric objects or text frames.


                Tables are amongst them!


                You cannot call a Table by its ID using pageItems.itemByID().

                To get a Table object by its ID you have to call it from its Story object.




                If you do not know what story the table lies in and you only know its ID value, you have to iterate through all stories of the document. And  through all cells of every table in case of nested tables (tables inside cells) to get what you want.



                • 5. Re: Select textframe on basis on textFrame.id
                  Laubender Adobe Community Professional & MVP

                  There are other ways of finding tables inside a document.
                  You could do a Text Search (NOT a GREP Search!) for the character that represents a table.

                  And compare every single result with the known ID of the table object you are looking for.


                  app.findTextPreferences = app.changeTextPreferences = null;
                  app.findTextPreferences.findWhat = "<0016>";
                  var tableCharacters = app.documents[0].findText();
                  var allTablesArray = new Array();
                  for(var n=0;n<tableCharacters.length;n++){
                      allTablesArray[n] = tableCharacters[n].texts[0].tables[0];
                  //DUMMY CODE:
                  //DEFINE myTableID before running this and ADD some action!
                  for(var t=0;t<allTablesArray.length;t++){
                     if(myTableID === allTablesArray[t].id){ }; //do something here and end the for-loop!
                  app.findTextPreferences = app.changeTextPreferences = null;




                  • 6. Re: Select textframe on basis on textFrame.id
                    Laubender Adobe Community Professional & MVP

                    @S. Hopkins (Shirley?) – since text frames could be nested (inside groups or otherwise, anchored obejcts etc.etc.), your function will only work on plain text frames on spreads.


                    To get (nearly) all text frames of a document, you have to iterate through the array of allPageItems.


                    In special cases, if MultiStateObjects are present, this is not enough. We recently had a discussion about that. Marc Autret provided a solution for that…



                    • 7. Re: Select textframe on basis on textFrame.id
                      DaveSofTypefi Level 2

                      I'm sorry, Laubender, I don't understand what you are trying to communicate to me that I didn't say myself. The issue I was addressing was the strange behavior of itemByID. If the id value in question is in use and you use itemByID with said id value then you will get that item returned to you no matter what it is, provided:


                      1. The object in question can be parented by the parent you start with.
                      2. Objects of the kind you say it is can also be parented by the parent you start with.


                      So, my first code worked because both Pages and PageItems can be parented by a document, but my second code didn't because Tables can't be.


                      A very interesting point about this is that you will get a valid object using this call even if the parent object you start with isn't actually the parent of the particular object you ask for. It is enough that if could be the parent. I can demonstate this using a document with two text frames and one table -- note: the two text frames are not threaded to each other.


                      var tableID = app.documents[0].stories[0].tables[0].id
                      var tableA = app.documents[0].textFrames[0].tables.itemByID(tableID);
                      $.writeln("tableA is valid: " + tableA.isValid);
                      var aParent = tableA.parent;
                      $.writeln("tableA is parented by: " + aParent.constructor.name + " " + aParent.id);
                      var tableB = app.documents[0].textFrames[1].tables.itemByID(tableID);
                      $.writeln("tableB is valid: " + tableB.isValid);
                      var bParent = tableB.parent;
                      $.writeln("tableB is parented by: " + bParent.constructor.name + " " + bParent.id);


                      This produces:


                      tableA is valid: true

                      tableA is parented by: TextFrame 237

                      tableB is valid: true

                      tableB is parented by: TextFrame 237


                      Thus, both itemByID calls produced the same table with the same parent text frame even though I used different text frames to find it.


                      The point here is that itemByID doesn't do any validty checking that the object is what you said it was and it assumes (unless you use getElements to sort out the result as I did in my first script) and the parent you start with is not necessarily the parent of the object you find.


                      Also, itemByID always returns an object even if one with the proffered id doesn't exist -- it just marks it invalid in that case.


                      Look at this sequence from the JS Console on the same document. I got a surprise:



                      Result: [object Page]


                      I asked for a page so it told me that what it returned was a page



                      Result: [object TextFrame]


                      But getElements reveals that it is in fact a text frame



                      Result: [object Page]


                      So I used a random number and again it said the result is a page



                      Result: true


                      This was the surprise. Apparently, 17 is actually in use for a real object



                      Result: [object Color]


                      And indeed it was a color.


                      So I tried again with a different random number and this time the id in question is not in use.



                      Result: [object Page]


                      Result: false



                      • 8. Re: Select textframe on basis on textFrame.id
                        Marc Autret Level 5

                        Hi all,


                        As Dave showed itemByID() is a great shortcut to get access to any DOM component from anywhere, the parent collection being just a placeholder (provided that any ID is an UID and gets direct access in the document database).


                        This interesting fact offers a powerful way to exhaustively inspect every DOM object of a target document:


                        // Collect everything!
                        // ====================================
                        const re = /\[object ([^\]]+)/i;
                        var coll = app.activeDocument.pageItems,
                            i = 1000, // should be increased for long documents
                            o, t, m, a = [];
                        while( i-- )
                            if(! (o=coll.itemByID(i)).isValid ) continue;
                            t = '' + o.getElements();
                            (m=t.match(re)) && (t = m[1]);
                            a[a.length] = 'ID' + i + ' => ' + t;
                        alert( a.join('\r') );





                        EDIT: Also, there are good reasons to believe that IDs are ordered in chronological creation order.

                        • 9. Re: Select textframe on basis on textFrame.id
                          virender_CTS Level 1

                          Hi Hopkins, Dave, Uwe, Marc,


                          Many thanks for your replies.


                          I will consider your replies while I will create the script to fit my requirements. I will post the final coding for your further review.



                          • 10. Re: Select textframe on basis on textFrame.id
                            Laubender Adobe Community Professional & MVP

                            @Dave – thank you so much to sorting this out.
                            I did not read your answer thoroughly enough. Just a misunderstanding…


                            @Marc – as always: splendid.

                            I already experimented with something like that; not so elaborate, but still…


                            To get the right value of "i" in your "Collect everything!", one could add a new object like a rectangle and use its ID value. Maybe adding 100 to it to be on the save side, but that should not be necessary.


                            //Adding a new object, reading its ID value, remove the new object:
                            var i = app.activeDocument.rectangles.add().id;
                            //Collect everything!
                            // "i" is already defined!



                            • 11. Re: Select textframe on basis on textFrame.id
                              Marc Autret Level 5



                              > To get the right value of "i" in your "Collect everything!", one could add a new object like a rectangle and use its ID value…