Copy link to clipboard
Copied
Hi,
I have one text frame with a word inside it on a layer called "Draft".
When I call:
var y = app.activeDocument.layers.itemByName( "Draft" );
alert( y.textFrames.length );
I always get 0.
Any idea why that is?
It's the same for textFrames.length.
Hi Jake,
for details see also this discussion from 5 years ago:
[JSX] Bug in property "allPageItems" of object "layer"?
Regards,
Uwe
Copy link to clipboard
Copied
I mean it's the same for allPageItems
Copy link to clipboard
Copied
Hi Jake,
is the textFrame positioned on a master spread on a layer named "Draft" ?
Then indeed:
app.documents[0].layers.itemByName("Draft").allPageItems.length
will return 0
Wheras:
app.documents[0].allPageItems.length
should return at least 1
Years ago we had a discussion about this here in the forum.
Some say it's a bug, some say it's as designed.
Regards,
Uwe
Copy link to clipboard
Copied
Hi Jake,
for details see also this discussion from 5 years ago:
[JSX] Bug in property "allPageItems" of object "layer"?
Regards,
Uwe
Copy link to clipboard
Copied
Thanks Laubender. You're right the text frame is on a master spread.
It definitely was confusing but the discussion was a great help!
I'll use your suggestion of:
1. check if: doc.mylayer.pageItems.length = 0
2. loop through all pageItems on master pages and looking if the itemLayer property is equal to the particular layer we want to remove
Copy link to clipboard
Copied
Yes, I was also confused back then after detecting that.
Glad I could help.
Regards,
Uwe
Copy link to clipboard
Copied
Hi Laubender,
I tried the following:
var doc = app.activeDocument;
var myLayer = doc.layers.itemByName( "Draft" );
alert(myLayer.name);
if ( myLayer.pageItems.length === 0 ) {
var masterItems = doc.masterSpreads.everyItem().pageItems;
for (var i = 0; i < masterItems.length; i++ ) {
alert (masterItems.itemLayer.name )
if ( masterItems.itemLayer.name === myLayer.name ) {
alert ("found it")
break;
}
}
}
The variable masterItems holds an array of pageItems but the name property is always undefined.
Any idea why?
Copy link to clipboard
Copied
jakec88782761 wrote
… Any idea why?
Yes.
Resolve your page items with getElements() .
That means address the concrete object—in your cae a textFrame—that is behind the pageItem.
Your code:
masterItems.itemLayer.name
// Result: Undefined
Resolving the page item:
// Resolving with getElements()
masterItems.getElements()[0].itemLayer.name
// Result: Draft
Best read about "On everyItem()" by Marc Autret:
Indiscripts :: On ‘everyItem()’ – Part 1
Indiscripts :: On ‘everyItem()’ – Part 2
Regards,
Uwe
Copy link to clipboard
Copied
Hi Jakec
(…)
The variable masterItems holds an array of pageItems (…)
Nope, it doesn't. As defined, your variable masterItems holds a PageItems object, that is, a collection.
What you want is
// ...
var masterItems = doc.masterSpreads.everyItem().pageItems.everyItem().getElements();
// ...
and then your code will work.
But your mistake is interesting to study. Your original assignment,
// ...
var masterItems = doc.masterSpreads.everyItem().pageItems;
// ...
leads to an object (typeof masterItems == "object") whose actual class is PageItems. In other words, (masterItems instanceof PageItems) == true. We know that PageItems is a collection, not an array. That kind of object exposes a length property though, returning the count of underlying elements (myCollection.length is equivalent to myCollection.count().)
But what is the interesting fact here? Well, you formed this PageItems (single) object from a syntax that embeds the famous everyItem() command, which is known to make up a plural specifier. Usually, adding a property after a plural specifier creates an array. So one could have expected …everyItem().pageItems to be an “array of PageItems collections,” which it is not. Furthermore …everyItem().pageItems is not a specifier either. Maybe we may call it an "incomplete specifier" in the sense that it just opens a branch in the path and is still waiting for something next to finalize an actual specifier. A simple way to prove that your masterItems is not a specifier—apart from the fact that it is a collection!—is checking the unavailability of masterItems.toSpecifier(). (=> Runtime error)
Anyway, and that's the point I wanted to highlight, masterItems.toSource() results in the string 'resolve("/document[@id=1]/master-spread/page-item")', which exactly contains the path that masterItems.everyItem().toSpecifier() would have shown before resolution. Intriguing, is not it? In addition, masterItems.count() returns the correct number of items that one will access using the complete syntax, meaning that this weird PageItems collection already integrates the effect of masterSpreads.everyItem() used upstream. I wasn't well aware of this phenomenon before considering this example, so, thanks for your typo 😉
@+
Marc
Copy link to clipboard
Copied
Thanks for the explanation Marc, I had a look at your blog posts about everyItem() and it was an interesting read.
One thing that does confuse me though:
var masterItems = doc.masterSpreads.everyItem().pageItems.everyItem().length
gives me an error "object does not support the property length. Even though there are 3 page items.
Copy link to clipboard
Copied
Thank you for the details, Marc.
Hi Jake,
I'm running your script with the ESTK on a document with 3 master spreads where a text frame is added to layer "Draft" on the 3rd master spread. I am doing a break point just before the loop through the masteritems.
Then I ask the Console some things by typing to it.
Hit return and the Console is answering with an immediate result.
masterItems
Result: [object PageItems]
// Note the plural s : [object PageItems]
masterItems.constructor.name
Result: PageItems
// Note the plural s : PageItems
masterItems.count()
Result: 0,0,1
// No pageItem on master 1 and 2. One pageItem on master 3.
masterItems.count().constructor.name
Result: Array
masterItems.length
Result: 1
masterItems.everyItem()
Result: [object PageItem]
// Note there is NO plural s this time!
masterItems.everyItem().constructor.name
Result: PageItem
// Note there is NO plural s this time!
masterItems.everyItem().getElements()
Result: [object TextFrame]
masterItems.everyItem().getElements().constructor.name
Result: Array
masterItems.everyItem().getElements().length
Result: 1
masterItems.everyItem().getElements()[0].itemLayer.name
Result: Draft
Regards,
Uwe
Copy link to clipboard
Copied
Thanks Laubender.
So, it seems that everyItem() when used with pageItems returns a single object, no matter how many page items are in the collection:
Copy link to clipboard
Copied
Uwe, there is even more to it.
While masterItems claims to be an [object PageItems], it actually targets a multitude of collections - the three of them that contribute to the array of three lengths. To me this is a distinctive type by its behaviour. This works along the same principle as when you access one property via the everyItem() of a collection - that also yields an array of the property values, one per individually targeted object.
You also have to consider that ExtendScript will outsmart you on the result for some special cases of a single target, it will deliver the actual value rather than its array wrapper. This is especially annoying with xmlElements.itemByName() - you expect a single element and it works most of the time, but yields an array in case of accidental extra elements with the same tag.
You actually benefit from a similar simplification when getElements() in your example returns the single array. It could have yielded an array of 3 arrays, one containing your actual text frame and the others empty.
For comparison, have a look at this one:
app.activeDocument.masterSpreads.everyItem().pageItems.itemByRange(0,1).contents.toSource()
Ergebnis: ["a", "A", "b", "B", "c", "C"]
Three master spreads, two text frames each - or is it a different combination? You could not tell from the result when you were using everyItem() instead of itemByRange().
If you have a language with strict type checking such as Java, this way you will end up with four roughly similar classes, that just differentiate by array-ness of target and result.
app.activeDocument.masterSpreads.everyItem().pageItems.item(0).contents.toSource()
Ergebnis: ["a", "b", "c"]
The 3 collections iterated, item(0) of each then contributes to the contents array.
Now think about the class specific properties object returned by the ".properties" property. Another related type, it just omits the methods. It would be interesting to see how the language bindings for C# and others deal with the mentioned cases, it took me several weekends to sort this out for Java.
Dirk
Copy link to clipboard
Copied
Wow, great and deep explanation!
> (…) To me this is a distinctive type by its behaviour (…)
I finally come to the same conclusion thanks to Uwe's tests and Dirk's interpretation. So there is a sneaky mistake in my previous message—which unfortunately I can no longer edit. It is not true that xyz.collection.count()===xyz.collection.length with all possible xyz specifiers.
Indeed, given
and
Uwe have discovered that xyz.collection.count() is an Array of numbers [z0, z1, …, zk], while xyz.collection.length is a single number N, such as N = Σ zk.
In our specific discussion we are talking about the entity myDoc.masterSpreads.everyItem().pageItems, whose type is the PageItems collection. Applying .length to it returns the total number of underlying page items, while applying .count() returns an array that still discriminates the count of page items for each masterspread.
var doc = app.activeDocument,
entity = doc.masterSpreads.everyItem().pageItems;
alert( entity.toSource() ); // resolve("/document[@id=1]/master-spread/page-item")
alert( entity.count() ); // [z0,z1...]
alert( entity.length ); // Σ(zk)
@+
Marc