Copy link to clipboard
Copied
var sel = app.documents[0].selection;
alert(sel); // [object TextFrame]
alert(sel.length) // 1
When my cursor is inside the text, I tried the code below and found both are [object TextFrame], but sel.length is invalid..
It should be noted that sel is undefined.
item = app.activeDocument.selection[0];
if (item.parent.textFrames[0].constructor.name == "TextFrame") {
var sel = item.parent.textFrames[0];
}
alert(sel);//[object TextFrame]
alert(sel.length)//undefined?????
@dublove yes definitely. That is the way I would code it. I had a quick search and I have already written a function (for you, back in 2022!). Here is an example usage:
/**
* @author m1b
*/
function main() {
var doc = app.activeDocument;
var sel = getTextFrames(doc.selection);
alert('sel = ' + sel + ' (length: ' + sel.length + ')');
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Test');
/**
* Returns any text frames found in given `items`
...
@dublove I wrote this function for getting the table. Not sure how good it is.
- Mark
function main() {
var doc = app.activeDocument;
var table = getTable(doc.selection);
alert(table);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Test');
/**
* Attempts to return a Table, given an object.
* @author m1b
* @version 2023-02-06
* @param {InsertionPoint|Text|Cells|Table|TextFrame} obj - an object related to a Cell.
* @returns {Cell}
*/
f
...
Copy link to clipboard
Copied
@dublove there's usually no difference between app.activeDocument and app.documents[0].
Your problem is simply that sometimes the line
var sel = item.parent.textFrames[0];
doesn't get called, due to the predicate, so it will throw an error.
If you want to handle the error gracefully, do it like this:
var item = app.activeDocument.selection[0];
// must define sel outside the if/else
var sel;
if (item.parent.textFrames[0].constructor.name == "TextFrame") {
sel = item.parent.textFrames[0];
}
else {
sel = { length: 0 };
}
alert(sel);
alert(sel.length);
Copy link to clipboard
Copied
Hi m1b.
As you mentioned, is the value of sel.length 0?
But sel is already an object [object TextFrame].
If that's the case, my sort(a,b) interaction below won't capture the objects and will throw an error.
Actually, I just want to automatically obtain the final TextFrame based on different selections.
alert(item.constructor.name);
var sel;
item = app.activeDocument.selection[0];
if (item.parent.textFrames[0].constructor.name == "TextFrame") {
sel = item.parent.textFrames[0];
}
else if (
item.constructor.name == "Image"
|| item.constructor.name == "TextFrame") {
sel = app.documents[0].selection;
}
sel.sort(function (a, b) {
var aBounds = a.visibleBounds;
var bBounds = b.visibleBounds;
if (aBounds[1] < bBounds[1]) {
return -1;
} else if (aBounds[1] > bBounds[1]) {
return 1;
}
else {
return bBounds[0] - aBounds[0];
}
});
Copy link to clipboard
Copied
Really helpful breakdown — I’ve run into similar issues where features behave differently depending on device or context. It reminds me how important it is to double-check workflows across platforms, whether it’s Excel or even how a website displays on mobile vs desktop. Consistency really makes or breaks productivity.
Copy link to clipboard
Copied
@m1b said: "there's usually no difference between app.activeDocument and app.documents[0]."
Hi @dublove ,
the difference between app.documents[0] and app.activeDocument is a subtle one.
It could be that the addressed document with app.documents[0] is a document without a layout window.
Not so with app.activeDocument. That is always one with a layout window.
Let's proof that; start InDesign with no document open and add two documents with a different parameter for showingWindow in method add():
// START InDesign WITH NO OPEN DOCUMENT:
// Add two documents:
var doc1 = app.documents.add(); // showingWindow parameter true is default
var doc2 = app.documents.add( false ); // showingWindow parameter set to false
alert( app.documents.length ) ; // returns 2
alert( app.activeDocument == doc1 ); // returns true
Regards,
Uwe Laubender
( Adobe Community Expert )
Copy link to clipboard
Copied
Too deep.
Can't grasp it all at once.
How do I make `sel = item.parent.textFrames[0];` into a real, controllable object?
It should be controllable like `sel = app.documents[0].selection;`.
I tried using
alert(app.documents[0].selection.parent);
but app.documents[0].selection doesn't seem to support parent.
It seems like alert(app.documents[0].selection[0].parent.textFrames[0]);
But sel.app.documents[0].selection[0].parent.textFrames[0];
still cannot be recognized as a real textFrame.
It remains a fake (logical textFrame?)
Copy link to clipboard
Copied
Hi @m1b @Laubender
In other words, the textFrame obtained through conversion is not a true selection object.
It is not a collection and does not have a length property.
Does it need to be converted into a collection?
This time sort(a,b) didn't trigger an alert.
But the subsequent alert(“A:” + sel[0].parentPage) is invalid.
@m1b Is it possible to have something similar to the image?
As long as the cursor is within the text box, always return the textFrame object.
var doc = app.activeDocument,
items = doc.selection;
var graphics = getGraphics(doc.selection);
function getGraphics(items) {
var graphics = [];
if ('Array' === items.constructor.name) {
for (var i = 0; i < items.length; i++)
graphics = graphics.concat(getGraphics(items[i]));
}
else if (
items.hasOwnProperty('allGraphics')
&& items.allGraphics.length > 0
)
graphics = graphics.concat(items.allGraphics);
else if (
items.hasOwnProperty('allPageItems')
&& items.allPageItems.length > 0
)
graphics = graphics.concat(getGraphics(items.allGraphics));
else if ('Image' === items.constructor.name) {
graphics.push(items);
}
return graphics;
};
Copy link to clipboard
Copied
@dublove yes definitely. That is the way I would code it. I had a quick search and I have already written a function (for you, back in 2022!). Here is an example usage:
/**
* @author m1b
*/
function main() {
var doc = app.activeDocument;
var sel = getTextFrames(doc.selection);
alert('sel = ' + sel + ' (length: ' + sel.length + ')');
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Test');
/**
* Returns any text frames found in given `items`.
* @author m1b
* @version 2025-09-19
* @param {Array<PageItem>} items - a DOM item or items to search in, eg. doc.selection.
* @returns {Array<TextFrame>}
*/
function getTextFrames(items) {
if (undefined == items)
return [];
var parentTextFrames = [];
var found = [];
var already = {};
if (!items.hasOwnProperty('0'))
items = [items];
for (var i = 0, item; i < items.length; i++) {
item = items[i];
if (
item.hasOwnProperty('pageItems')
&& item.pageItems.length > 0
)
found = found.concat(getTextFramesFromInside(item.pageItems.everyItem().getElements()));
if (
item.hasOwnProperty('parentTextFrames')
&& item.parentTextFrames.length > 0
)
// is contained by textFrame(s)
found = found.concat(getTextFrames(item.parentTextFrames));
if (item.hasOwnProperty('parentStory'))
// is in a story, which may contain textFrames
found = found.concat(getTextFrames([item.parentStory]));
// has a parent which might be a textFrame
if (item.hasOwnProperty('parent')) {
while (
item.hasOwnProperty('parent')
&& 'Document' !== item.constructor.name
) {
if ('TextFrame' === item.constructor.name)
parentTextFrames.push(item);
if (
item.hasOwnProperty('parentTextFrames')
&& item.parentTextFrames.length > 0
)
parentTextFrames = parentTextFrames.concat(item.parentTextFrames);
item = item.parent;
}
if (parentTextFrames.length > 0)
found = found.concat(parentTextFrames);
}
}
// unique text frames only
var uniqueFrames = [];
for (var i = 0; i < found.length; i++) {
if (!already[found[i].id]) {
uniqueFrames.push(found[i]);
already[found[i].id] = true;
};
}
return uniqueFrames;
/** Returns text frames from inside the `items` */
function getTextFramesFromInside(items) {
var found = [];
if (!items.hasOwnProperty('0'))
items = [items];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (
item.hasOwnProperty('pageItems')
&& item.pageItems.length > 0
)
found = found.concat(getTextFramesFromInside(item.pageItems.everyItem().getElements()));
else if ('TextFrame' === item.constructor.name)
found.push(item);
}
var textFrames = [];
for (var i = 0; i < found.length; i++)
if ('TextFrame' === found[i].constructor.name)
textFrames.push(found[i]);
return textFrames;
};
};
Edit 2025-09-05: fixed bug in getTextFrames function that caused stack overrun.
Edit 2025-09-19: improvement to—hopefully!—get more text frames in more cases.
Copy link to clipboard
Copied
Hi m1b.
You're incredibly formidable, far ahead of your time.
Back in 2022, I didn't even have the capacity to recognize this issue existed.
This is perfect. The problem is solved in an instant.
Thank you very much.
Copy link to clipboard
Copied
Hi @m1b
Is there a table version?
Whether you select text within a cell, select a cell, or select a table, it always returns "Table".
When the cursor is inside a table, I don't want to get textFrame.
Copy link to clipboard
Copied
@dublove I wrote this function for getting the table. Not sure how good it is.
- Mark
function main() {
var doc = app.activeDocument;
var table = getTable(doc.selection);
alert(table);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Test');
/**
* Attempts to return a Table, given an object.
* @author m1b
* @version 2023-02-06
* @param {InsertionPoint|Text|Cells|Table|TextFrame} obj - an object related to a Cell.
* @returns {Cell}
*/
function getTable(obj) {
if (obj == undefined)
return;
if ('Array' === obj.constructor.name) {
for (var i = 0, table; i < obj.length; i++) {
table = getTable(obj[i]);
if (table && table.isValid)
return table;
}
return;
}
if (obj.constructor.name == 'Cell')
return obj.parent;
if (obj.parent.constructor.name == 'Cell')
return obj.parent.parent;
if (
obj.hasOwnProperty('cells')
&& obj.cells.length > 0
)
return obj.cells[0].parent;
if (
obj.hasOwnProperty('tables')
&& 0 !== obj.tables.length
)
return obj.tables[0];
};
Copy link to clipboard
Copied
There's an issue:
If I currently have an image selected,
getTextFrames(doc.selection) does not return null. Instead, it incorrectly displays my entire script code.
getGraphics(doc.selection) does not seem to have this issue. Even if I select text, it returns null(Nothing unusual occurred.).
In other words, if getTextFrames() fails to retrieve results, it will throw an error.
var doc = app.activeDocument;
var items = doc.selection;
var grs = getGraphics(doc.selection);
var tfs = getTextFrames(doc.selection);
Copy link to clipboard
Copied
Thanks @dublove, I have corrected the bug in my getTextFrames function.
- Mark
Copy link to clipboard
Copied
Hi m1b.
Thank you very much.
This morning I used `graphics.length > 0 for the check.
Now ,haver replaced TextFrames, no exceptions.
Can TextFrames be divided into two scenarios:
A: Cursor inside the table.
B: Cursor outside the table.
Because sometimes when the cursor is inside the table, I don't want the text box to be affected.
Alternatively, it can be distinguished like this:
if(getTextFrames && !table) {
}
Copy link to clipboard
Copied
Hi @m1b
If a text box is selected, it contains two tables.
getTable(doc.selection); only returns one table.
Is it possible to restrict the return to all tables within the text box, or all tables in the Story?
Additionally, when the cursor is within a cell, `getTable(doc.selection)` is not a collection and does not have a length property.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
If I select an image within text, can I return a TextFrame?
Copy link to clipboard
Copied
Hi @dublove ,
if you select an object, a graphic frame for example, that is anchored to text, you will get the anchor character with:
app.selection[0].parent
If you'd select the image inside the graphic frame, then do:
app.selection[0].parent.parent
The anchor character's first or second insertion point could be asked to reveal the text frame:
app.selection[0].parent.parent.insertionPoints[0].parentTextFrames[0]
Kind regards,
Uwe Laubender
( Adobe Community Expert )
Copy link to clipboard
Copied
Hi @Laubender
I want to modify the getTextFrames method in @m1b.
But I don't understand it.
Copy link to clipboard
Copied
> If I select an image within text, can I return a TextFrame?
Yes, here is an example:
(function () {
var doc = app.activeDocument;
var textFrame = getParentTextFrame(doc.selection[0]);
alert(textFrame);
})();
/**
* Returns a parent text frame, where possible,
* given a page item.
* @author m1b
* @version 2025-09-19
* @Param {PageItem} item - an Indesign page item.
* @Returns {TextFrame?}
*/
function getParentTextFrame(item) {
if (!item)
return;
while (item.hasOwnProperty('parent')) {
if ('TextFrame' === item.constructor.name)
return item;
if (
item.hasOwnProperty('parentTextFrames')
&& item.parentTextFrames.length > 0
)
return item.parentTextFrames[0];
item = item.parent;
}
};
Copy link to clipboard
Copied
Hi @m1b
Should we create another one? Too many are hard to control.
Can we merge it into the function getTextFrames(items)?
Copy link to clipboard
Copied
Oh I understand. I think it will work. I have hopefully improved the function above.
- Mark
Copy link to clipboard
Copied
Thank you.
It's bound to get better and better.
When I have time, I'll have to take a closer look at how you pulled it off.
Find more inspiration, events, and resources on the new Adobe Community
Explore Now