• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

.isValid loop doesn't work

Community Beginner ,
Jan 03, 2019 Jan 03, 2019

Copy link to clipboard

Copied

Hi,

I'm writing my very first script, so this is probably a very basic question, but please bear with me. I am trying to change the color of all objects with the name 'Rood' in a specific layer. Now I was thinking to use an if statement to look for items called Rood, if it finds any, change their name and color and check again. When it doesn't find any Rood layers it should break out of the loop.

This is the snippet I wrote:

     if (myEDlayer.pageItems.itemByName ("Rood").isValid) { //if there is an item called 'rood'

        myEDlayer.pageItems.itemByName ("Rood").name = "Blauw"; //make the name 'blauw'

        myEDlayer.pageItems.itemByName ("Blauw").fillColor = "Blauw (C=100, M=66, Y=0, K=0)"; //and change the color to blue

        }

The problem is it only works for the first item called Rood and leaves the others alone. This makes me think I haven't wrote the .isValid part correctly. Could someone help me understand where I'm going wrong?

Thanks!

TOPICS
Scripting

Views

782

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Guide , Jan 03, 2019 Jan 03, 2019

Actually this is a complicated issue, and feels like multiple InDesign bugs to me.

When you use ...item... functions on collections, in principle you can target multiple objects. A prominent example is the xmlElements collection with itemByName("myTag"), another more obvious example is the everyItem() function.

So I checked whether this also works for page items with the same name, but it doesn't.

$.writeln(myEDlayer.pageItems.itemByName ("Rood").getElements().length);

=> 1

getElements() should yield

...

Votes

Translate

Translate
Community Expert ,
Jan 03, 2019 Jan 03, 2019

Copy link to clipboard

Copied

Hi,

Lets try and understand your code, now you queried the pageItems collection of the layer for an object with name "Rood" and then changed its name fillColor. So far so good, once this is done then we still need to add instructions in the code to keep looking for the next object in the layers pageItems collection with the name "Rood", this part of code is missing in your existing code.

Now this new thing can be done in two ways

  • Iterate each element of the pageItems collection of the layer and check if its name is "Rood" if yes the do the required operation, if no move on to the next object. At the end of this iteration we would have processed each object and dealt with it as needed
  • We could reuse the code you have and repeat it untill myEDlayer.pageItems.itemByName ("Rood").isValid returns false. A do while loop is suitable in this case.

Try any one of these two approaches and let us know if it worked.

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 03, 2019 Jan 03, 2019

Copy link to clipboard

Copied

Actually this is a complicated issue, and feels like multiple InDesign bugs to me.

When you use ...item... functions on collections, in principle you can target multiple objects. A prominent example is the xmlElements collection with itemByName("myTag"), another more obvious example is the everyItem() function.

So I checked whether this also works for page items with the same name, but it doesn't.

$.writeln(myEDlayer.pageItems.itemByName ("Rood").getElements().length);

=> 1

getElements() should yield an array with all your candidate page items for the specifier expression itemByName() ....

When you run your script for the first time, select the renamed page item - in my case the other one with the original name was colorized. I'm not sure why, but smells like a caching problem, definitely another bug. It should have changed the color for the renamed item.

Another variant of the script that ran thru for me without the crash in the second run:

var rect = myEDlayer.pageItems.itemByName ("Rood").getElements()[0];

if (myEDlayer.pageItems.itemByName ("Rood").isValid) { //if there is an item called 'rood' 

        myEDlayer.pageItems.itemByName ("Rood").name = "Blauw"; //make the name 'blauw' 

  myEDlayer.pageItems.itemByName ("Blauw").fillColor = app.activeDocument.swatches.lastItem(); // my blue

}

the added first line var rect ... getElements() somehow changed the situation - but the color still modifies the wrong item.

One contributing factor could be that "pageitem" is a base class - your page items are actually rectangle or text frame or ...

if (myEDlayer.rectangles.itemByName ("Rood").isValid) { //if there is an item called 'rood' 

        myEDlayer.rectangles.itemByName ("Rood").name = "Blauw"; //make the name 'blauw' 

  myEDlayer.rectangles.itemByName ("Blauw").fillColor = app.activeDocument.swatches.lastItem();

}

Worked for me without exception, but still with the color at the wrong item.

In the short run you can work around the problem by modifying multiple properties with one assignment:

while(myEDlayer.pageItems.itemByName ("Rood").isValid)

myEDlayer.pageItems.itemByName("Rood").properties = {

  name : "Blauw",

  fillColor: app.activeDocument.swatches.lastItem()

};

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jan 04, 2019 Jan 04, 2019

Copy link to clipboard

Copied

Interesting observations Dirk Becker, i investigated a bit myself and found the following

  • I had not observed that itemByName did return multiple objects. But its a strange kind of object that we get in case of xmlElements.itemByName, we still get a single xmlElement object, not an array, but the properties inside it are arrays.
  • With regards to the issue that the color is changed of the wrong object, my observation is that if we have another object with the same name "Blauw" in the document up in the order than the one we are changing, the statement myEDlayer.pageItems.itemByName ("Blauw") returns that element and hence its color is changed, not of the one whose name we set just before this statement. I think this makes sense and is not a caching issue. This also explains why things work fine if you set the property all at once on the same object.
  • The following code also works if you save the object in a variable and then set the properties, i wonder why in this case the object is not resolved every time the property is accessed, which should have resulted in the wrong result

var myEDlayer = app.activeDocument.layers[0]

if (myEDlayer.pageItems.itemByName ("Rood").isValid)

{ //if there is an item called 'rood'

     var a = myEDlayer.pageItems.itemByName ("Rood")

     a.name = "Blauw"; //make the name 'blauw'

     a.fillColor = app.activeDocument.swatches.lastItem(); //and change the color to blue

}

Screen_Shot_2019-01-04_at_2_16_46_pm.png

However if the initial state of the document would have been such that the object Rood was upper in order than object Blauw, then the coloring works fine

So finally the solution would be to put the code in loop and either set the properties in one shot as suggested by Dirk, or hold the object in a variable and use that to set the properties.

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 04, 2019 Jan 04, 2019

Copy link to clipboard

Copied

I hope Jeroen won't mind if we carry on the discussion.

Manan, the properties of those plural specifiers from the XML elements don't just return arrays, they are also writable. Same with methods. This works the same way as with everyItem(), or itemByRange.

var blue = app.activeDocument.swatches.lastItem();

app.activeDocument.pageItems.everyItem().fillColor = blue;

You can even work across multiple such expressions:

app.activeDocument.stories.everyItem().textFrames.everyItem().characters.firstItem().contents = "x";

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jan 04, 2019 Jan 04, 2019

Copy link to clipboard

Copied

Dirk, you are right about that, i did try and change the content of multiple objects using the content property and it worked just fine. I have used everyItem this way quite a bit but this xmlElement object was something new that i discovered today from your comment. However do you concur on my finding regarding the object order causing the problem of color being applied to the wrong object? Also any thoughts on why saving the object in a variable worked fine, isn't it supposed to resolve the object again even in this case.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 04, 2019 Jan 04, 2019

Copy link to clipboard

Copied

https://forums.adobe.com/people/Dirk+Becker  schreef

I hope Jeroen won't mind if we carry on the discussion.

Not al all. This is all going way over my head, but carry on!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jan 04, 2019 Jan 04, 2019

Copy link to clipboard

Copied

LATEST

Hi together,

see also into this old thread where huge changes in DOM between CS4 and CS5 were discussed.

The behavior of pageItem.itemByName("Name") or pageItem.item("Name") had changed dramatically with CS5:

Re: With CS5, some things have changed

See also what Dave Saunders is doing there with app.scriptPreferences.version !

So, if you create an object reference while working within the CS4 versioning engine it won't work when you move back into the CS5 engine. So, by converting to an array--by using getElements()--which is a core feature, you can pass objects effectively from one engine to the other.

Just to increase your confusion, jeroenh14992699 …

Regards,
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 04, 2019 Jan 04, 2019

Copy link to clipboard

Copied

Thanks both, very helpful. I first tried a do-while loop as Manan suggested, but that only changed the name, not the color. Which is weird, cause it does mean the loop is working. Might be a bug as Dirk said. Anyways, tried Dirk's solution and that worked perfectly. Final snippet is this:

        while (myEDlayer.pageItems.itemByName ("Rood").isValid) { //if there is an item called 'rood'

        myEDlayer.pageItems.itemByName("Rood").properties = {

          name : "Blauw",

          fillColor: "Blauw (C=100, M=66, Y=0, K=0)"

        };

        }

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines