Single Javascript Function Question

I asked a similar question to this which gave what appeared to be great answers but I don’t think I asked the initial question properly so I will try again :slight_smile:

I am using Vanilla Javascript, no React, no web pack just plain simple Javascript with one index.js file.

I am also using API version 2 and Manifest 5

Use Case:
I have 12 buttons on my plugin and each time one is pressed, I want to see if the active selected layer name = Paint Here

Through reading, I have managed to make a Loop which loops all layers and returns the layer name of the selected layer.

Question:
Rather than having to write this loop out for each of the 12 buttons, is it possible to wrap the loop in a single function and then call that function from each of the 12 buttons.

I have tried the following but It always returns undefined

function checkName(y){
	
  const name = app.activeDocument.layers;

  // loop through all layers looking to see if the selected layer 
  //name includes Paint Here
	
  Array.from(name).forEach(function (x) {

	  if (x.selected == true) {
         
         if (x.name.includes(y)) {			
          return "yes"          
          }
       }
   
   });
}

//Call the function
console.log(checkName("Paint Here"));

Code works fine for me. If it returns undefined for you it’s due to one of the two reasons:

  • the layer you try to find isn’t selected
  • the name doesn’t match (string.includes is case sensitive)

Besides that, the code is kind of inefficient. You’re only interested in selected layers, so there’s no need to loop all (toplevel) layers. Also, you can call forEach() on the proxy directly and don’t need to wrap it in Array.from. Lastly, your expected result is some kind of affirmation / negation. Instead of returning a string ("yes") or undefined, wouldn’t it be more plausible to have a boolean return type?

Besides that, your variable names are a bit misleading or hard to understand. name is effectively layers and the y parameter could be name or searchedName.

With all that applied, you could have it as a one-liner:

const layerExists = (name) => app.activeDocument.activeLayers.some(layer => layer.name.includes(name))

Explanation: Array.some() is like forEach but it checks against the condition and returns true/false, when at least one element is found. It also stops the loop then and skips all the rest.

You can then work with it like:

if(layerExists("Paint here")) {
  // do stuff
}

Might be worth mentioning also that your current string check is quite broad since you use string.includes. Something like "Awesome Layer Paint here please" would also match the condition, also it’s probably unlikely someone has a layer with such a name. A more strict check for equality would be:

const layerExists = (name) => app.activeDocument.activeLayers.some(layer => layer.name === name)

To answer your original question: Yes, you can of course reuse a function you declared once in multiple button click handlers.

Btw, if you want to do some operations on that “Paint here” layer further down in your script, you might also want to check if only one layer is selected. Otherwise it might break if the user has multiple selected layers.

@simonhenke Thanks for the explanation and examples.

Using your cleaner version, in the button click event, I now have

const layerExists = (name) => app.activeDocument.activeLayers.some(layer => layer.name === name)

    if (layerExists("paint here")) {
      console.log("Layer is there and selected");
    }

Question
As an experienced coder, would you simply write the above in each button click event now its a 1 liner or would you create a global function and call that function from each button click ?

Also…

if (layerExists("paint here")){
}

Am I correct in thinking that the layer name has to be exact of what we are looking for and is also case sensitive ?

Performance-wise it doesn’t really matter, but the point of outsourcing code in reusable functions is to write less code in other places, so I’d keep it outisde.

As mentioned, string.includes just checks if the match-string is anywhere inside the other string, not equality.

I see Ian changed code to use the suggested layer.name === name, so yes, it’s exact and case sensitive

Oops, should had scrolled to the right in the code window :sweat_smile:

All suggested code now working in an outside function.
Having stepped through the code in the debugger I now feel as I have a better understanding how it is working.

The Array.some() was new to me and I will need to study this in more detail to get a better understanding of it.

many thanks for your help. I’m sure there will be others who will find it useful even though it may have been a simple question to some :slight_smile:

1 Like