Ok, I understand you now!
Firstly , eventListener sequence, this is getting a bit beyond my JS knowledge but as I understand it this is determined by the browser/client. Modern browsers go in ascending order, but some older browsers go in reverse (IE9 or Netscape). I don’t know about UXP, but based on your experience it sounds like ascending order.
There’s more to it than just that, and is perhaps a bit too much to go into here, but I suggest you go and have a look at event capture and bubbling.
Anyway, that’s not actually that relevant as I think there’s a better approach to your use case.
Rather than having three event listeners for what is essentially a single process is a bit messy, let’s wrap it all up in one event listener that dynamically changes functionality based on the button id.
The key to this is that batchPlay takes an array of action descriptors as it’s first argument and by using spread operators we can construct that array by combining other arrays into one.
// Index.html
<sp-button id="btn1" class="btn">Btn 1</sp-button>
<sp-button id="btn2" class="btn">Btn 2</sp-button>
<sp-button id="btn3" class="btn">Btn 3</sp-button>
// Index.js
// Imports
const { core } = require("photoshop");
const batchPlay = require("photoshop").action.batchPlay;
// Define generic action descriptor arrays that are the same for every button click.
// Each array is returned from a function so that newLayerName can be passed as an argument when the function is called and applied appropriately to the array.
const preActions = (newLayerName) => {
return [
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "back" }],
"makeVisible": false,
},
{
"_obj": "set",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "targetEnum" }],
"to": { "_obj": "layer", "name": newLayerName },
},
];
};
const postActions = (newLayerName) => {
return [
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "back" }],
"makeVisible": false,
},
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "forwardEnum" }],
"makeVisible": false,
},
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "forwardEnum" }],
"makeVisible": false,
},
{
"_obj": "set",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "targetEnum" }],
"to": { "_obj": "layer", "name": newLayerName },
},
];
};
// Define context specific actions. Again, returned from a function so that a variable can be passed in as an argument and applied to the array/s.
// This is a bit more complicated than the previous two; what we are returning here is an array of objects, each representing the set of actions to be performed for a specific button.
// Note that each object has two properties: an id (which corresponds to the id of a button), and an array of action descriptors.
// When we call this function later we will filter the results for the entry with the id that matches the id of the button clicked.
// In this example they all have the same action descriptors and only the id varies, and like your example assumes at least three layers are present in the active document.
const contextSpecificActions = (newLayerName) => {
return [
{
"id": "btn1",
"actions": [
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "back" }],
"makeVisible": false,
},
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "forwardEnum" }],
"makeVisible": false,
},
{
"_obj": "set",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "targetEnum" }],
"to": { "_obj": "layer", "name": newLayerName },
},
],
},
{
"id": "btn2",
"actions": [
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "back" }],
"makeVisible": false,
},
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "forwardEnum" }],
"makeVisible": false,
},
{
"_obj": "set",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "targetEnum" }],
"to": { "_obj": "layer", "name": newLayerName },
},
],
},
{
"id": "btn3",
"actions": [
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "back" }],
"makeVisible": false,
},
{
"_obj": "select",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "forwardEnum" }],
"makeVisible": false,
},
{
"_obj": "set",
"_target": [{ "_enum": "ordinal", "_ref": "layer", "_value": "targetEnum" }],
"to": { "_obj": "layer", "name": newLayerName },
},
],
},
];
};
// Get all button elements
const buttons = document.querySelectorAll(".btn");
// Add event listener to each button - we do this in a for...of loop as it allows asynchronous code within (forEach does not)
for (const btn of buttons) {
// Make event listener async so we can await executeAsModal
btn.addEventListener("click", async (event) => {
// Get the id of the button clicked from the event. You could even just make this const btnId = event.target.id
const target = event.target;
const btnId = target.id;
// Within the batchPlay call we construct the array of action descriptors using spread operators.
// We call each of our functions that return action descriptor arrays, passing in the newLayerName value.
// In the case of the contextSpecificActions call we use array.find() to get the entry that matches the clicked button id, and then .actions to just get the array of action descriptors from the returned object.
// The end result is a single array, unique to the button clicked.
await core.executeAsModal(async () => {
return batchPlay(
[
...preActions("Pre Actions"),
...contextSpecificActions("Contextual Actions").find((x) => x.id === btnId).actions,
...postActions("Post Actions"),
],
{}
);
});
});
}
I think you’ll agree that this is a cleaner approach which is also much more scalable - adding a new set of contextual actions is as easy as adding an entry to the array and popping a new button in the UI.
In regards to your questions about asychronous code; you’ll note that the only async code is for the executeAsModal
function call - this is so that the JS engine waits until batchPlay returns before moving on to any subsequent code (which in this example there is none). It has no bearing on the sequence in which the action descriptors are run, that is defined by their order in the array passed to batchPlay.