How To Loop Through Layers

From a button click I would like to loop through all layers and delete any Layer Syles which have a Color Overlay.

Can anyone suggest a way to achieve this please.

I think layer styles aren’t accessible via DOM yet, so you’ll have to use BatchPlay to check and delete them.

You could probably iterate the Layers collection of the DOM and get their id for further processing, or if you want to do a full BatchPlay approach you could read the numberOfLayers property of a document.
Once you know how many layers exist, you can loop over them for example using a for loop like

for(let idx = 1; idx <= numberOfLayers; idx++) {
   // do stuff on each layer
}

To check for a color overlay style, you’ll first of all need to read the layerEffects property:

const fx = photoshop.action.batchPlay([
  {
    _obj: 'get',
    _target: [{ _property: 'layerEffects' }, { _ref: 'layer',_index: idx}],
  },
], { synchronousExecution: true })[0]['layerEffects']

If you iterate via IDs, you can also reference a layer by ID via { _ref: 'layer',_id: *id*} instead.

The layer has a ColorFill, if this condition is true:
fx?.solidFill?.enabled || (!!fx?.solidFillMulti && fx.solidFillMulti.some(e => e.enabled))

The questionmarks in that code probably don’t work for you if you don’t use Typescript. It’s called “optional chaining” and checks for undefined values. Without it you’d have to check if each of the objects is undefined also, to prevent errros:
if(fx && fx.solidFill && fx.solidFill.enabled ...) and so on.

Once you found the relevant layers, you can delete the layer style. I’m not sure if there’s a function to delete a specific one, I think you can only do that using the setter for the layer effects in general. So you can simply delete the solidFill or solidFillMulti property from the layerEffects object, and pass it to the setter. Here’s the descriptor for it

export function __setLayerEffects(layerEffects, idx: number) {
  return {
    _obj: 'set',
    _target: [
      {
        _ref: 'property',
        _property: 'layerEffects',
      },
      { _ref: 'layer', _index: idx}
    ],
    to: layerEffects,
  }
}

@simonhenke Thankyou for the explanation, this is going to be very useful in a number of ways as it explains the process nicely.

Having played around with some of your code, it reminded me that in the solid color object, green is reported from photoshop as grain.

Now I dont know if thats deliberate or a typo but lets just hope they dont decide oneday to change it to green :slight_smile:

IIRC, you can use green also. grain instead of green is a leftover from old days where it was just GR (I think) :slight_smile:

Just ran a few tests and green shows undefined but grain shows the true value so it looks as though you have to use grain.

Green and grain both share the same char code "Grn ". BatchPlay is like a middleman doing the char code <-> string code conversion, as strings are more readable. So if you get a descriptor, you’ll have to access the property via .grain. If you set it however, it shouldn’t matter if you call the property grain or green, as both will be converted back to "Grn " again.

1 Like

Using the code supplied from @simonhenke above, I can now loop through all layers and identify the ones which have the SolidFill enabled.

Using Alchemist, this code removes the SolidFill color, I tried to replace the _index to idx but I get undefined returned.

Can anyone see anything obvious

batchPlay(
[
   {
      _obj: "disableSingleFX",
      _target: [
         {
            _ref: "solidFill",
            _index: 1 // tried replacing this with idx
         },
         {
            _ref: "layer",
            _enum: "ordinal",
            _value: "targetEnum"
         }
      ],
      _options: {
         dialogOptions: "dontDisplay"
      }
   }
],{
   synchronousExecution: false,
   modalBehavior: "fail"
});

That’s the solidFill effect you’re referencing by index there ( a layer can have multiple ones), not the layer.
If you want to target a specific layer by index, you’ll have to adjust the layer reference of the _target array:

{
  _ref: "layer",
  _index: idx
}

Thank You, that worked and I have now managed to condense the code a bit more to

batchPlay(
				[{
					_obj: "disableSingleFX",
					_target: [{
							_ref: "solidFill"
						},
						{
							_ref: "layer",
							_index: idx
						}
					]
				}], {
					synchronousExecution: false,
					modalBehavior: "fail"
				});