Photoshop UXP ScriptのUIをVanilla JSでこなす
Photoshop UXPでExtendScriptのようにScriptができるようになりましたがあまり盛り上がっている気もしません。実際どうなのでしょうか。
それは置いといてUXP Scriptもダイアログ上にUIを作ることも可能なのでExtendScriptのようにダイアログのあるScriptも可能なのですがこのUI、現状HTMLとCSSで構築しなければならずしかもJS上で全て組み立てないといけないのでExtendScriptのdialogUIに比べてハードルが高くなっています。勿論Reactなんかを使った方が便利ではあるのですがpsjsファイル単体、また簡単なファイル構成で動くでメリットを最大限に活かすためにあえてVanilla JSだけでやってみます。
JSでHTMLを弄るには
ご存知の通りJSでHTMLを触るにはinnerHTMLで直接string型のHTMLを代入する方法とcreateElementとappendChildのように要素をメソッドで作成して挿入するやり方があります。innerHTMLは操作こそ簡単ではありますがstring型で全て管理するのとコードの見通しが悪くなるのと最近セキュリティ的にinnerHTMLはあまり良くないみたいな話も聞きます。実際のところinnerHTMLが将来的に非推奨になるかどうかなんかはよくわかりませんがただ直接書いたHTMLをそのまま挿入できるのって触ってて以前から少し怖いと感じることもありました。ただ既に完成したHTMLとCSSをそのままinnerHTMLでbodyにブッこむのは楽な方法ではあるので選択肢の一つにはなると思います。
オブジェクト単位で各HTML要素を管理する
innerHTMLは置いといて実際にはcreateElementとappendChildメソッドを使ってオブジェクト単位でHTML要素を管理したいと思います。構造としてはDOM ツリーをそのままオブジェクトで表すようにDOMの親要素に対して子要素、孫要素があるようにオブジェクトに対して親と子のようにツリー形式で管理します。以下のように。
まぁReactのような構造ですね。勿論そこまでガッツリ作りませんが。まずcreateElement要素を作成したHTML要素をclassオブジェクトで包んで要素に再度アクセスする場合はclassメソッドを通じて中身にアクセスします。具体的には以下のようなベースのクラスを作ります。
constructorでelmの値をcreateElementメソッドで帰ってきた値を受け取ります。classNameとidも受け取ってcreateElementメソッドで作成したHTML要素に加えます。
header要素を作成するためにelmプロパティにdocument.createElement(‘header’)を渡しています。このHeaderArea classもそうですがこれから作成するHTML要素は全てこのHTMLDocBase classを継承します。作成したHTML要素に対して子要素を追加する場合もHTMLDocBase classから継承したaddElementsという独自のメソッドを使います。またCSSについて気付いた方もいると思いますがReactのCSS in JSのようにオブジェクトとして管理しています。一つづつstyleプロパティに代入するのはしんどいのでオブジェクト形式で使い回ししたりできるようにします。ただ問題がこのHTMLのstyleプロパティですがこれが実はプロパティではなくsetterメソッドなのでオブジェクトをそのまま代入できません。なのでHTMLDocBase classではObject.entriesの繰り返し構文でオブジェクト形式のCSSをまとめて代入するようにします。
そしてこのHeaderArea classはheaderの要素を管理するclassですが現在まだheader内に見出しも何も要素がない空っぽの状態です。なので試しに見出しを作成して追加してみましょう。
まず見出し要素のclass。
その後header要素と見出しの要素を作成してheader要素のaddElementsで子要素として見出し要素を追加します。
addElementsメソッドでは要素と同時にそれを包むclassもchildrenプロパティに追加しています。これで親classから子classにアクセスしやすいようにします。
idやclassNameを通じて親要素を包むclassからアクセスできるメソッドです。
イベント
ご存知のように各HTML要素にはそれぞれイベントを登録できます。しかし同じイベントのアクションで異なるコールバックを登録したりイベント削除する時に一々同じイベント名とコールバックを呼び出すのは面倒です。なのでイベントのアクション名とコールバックを組み合わせでオブジェクトにしてプロパティにSetオブジェクト内に格納しています。これでクリックのイベントを呼び出したい時は単にthis.events.has(evetName)のようにするだけで既に登録されているかどうか調べられます。
こんな感じでclass化したHTML要素を親から子へと追加して、イベントも追加してUIを作成します。以下フッターを作るサンプルです。
ExtendScriptのダイアログUIが単純なUIなら簡単に作成できたのでUXPでもVanilla.jsだけででできる限りUIを作成しようとはやってみましたが
結局やればやるほどTypeScript、Reactが使いたくなってきました。ガチのフロントエンジニアだったらさらにスマートにUIを組むこともできるのかもしれませんが。