はじめに
Phoenix LiveView で Web ページを作っているとき、クリック時のイベント伝搬を止めたいときがあります
具体的に言うと、例えばモーダルとフラッシュ(通知)が表示されているときです
mix phx.gen.live で生成される通常のページの場合、モーダルが閉じてフラッシュが表示されるため、この状態にはなりません
フラッシュ表示後もモーダル内で続けて操作するようなケースを想定しています
通常、モーダルの外側をクリックするとモーダルが閉じます
また、フラッシュはフラッシュ自体をクリックすることで閉じます
モダールとフラッシュが両方表示されているとき、フラッシュを閉じるとモーダルまで一緒に閉じてしまいます
これはフラッシュをクリックしたイベントが、その後ろにある「モーダルの外側」にまで伝わってしまっているためです
フラッシュだけ閉じたい場合、クリックイベントをフラッシュより後ろに伝わらないよう、イベントの伝搬を止める必要があります
JavaScript の場合、以下のような実装で実現可能です
elemnt.addEventListener('click', (event) => {
event.stopPropagation()
....
})
stopPropagation
により、イベントの伝搬を停止しています
これを Phoenix LiveView で再現します
phx-click による処理
mix phx.new
で生成される core_components では、フラッシュコンポーネントが以下のように定義されています
lib/app_web/components/core_components.ex
...
<div
...
id={@id}
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
...
>
phx-click
でクリック時の処理を定義しています
{}
内は Elixir であり、 JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")
でフラッシュを閉じています
Elixir 側の処理なので、 phx-click
の中で stopPropagation
は実行できません
フックを経由した処理
フックを以下のように定義します
assets/js/app.js
+ const FlashHook = {
+ mounted () {
+ this.el.addEventListener('click', event => {
+ event.stopPropagation()
+ liveSocket.execJS(this.el, this.el.getAttribute('data-flash'))
+ })
+ }
+ }
+
- let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
+ let liveSocket = new LiveSocket('/live', Socket, {hooks: {FlashHook}, params: {_csrf_token: csrfToken}})
this.el.addEventListener('click', event => { ... })
でクリック時の処理を指定しています
event.stopPropagation()
によってイベントの伝搬を止めます
liveSocket.execJS(this.el, this.el.getAttribute('data-flash'))
で data-flash
属性に指定した処理を実行します
lib/app_web/components/core_components.ex
...
<div
...
id={@id}
+ phx-hook="FlashHook"
- phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
+ data-flash={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
...
>
phx-hook
でコンポーネントにフックを紐付け、 phx-click
を data-flash
属性に変更します
これにより、今までクリック時に直接実行していた処理がフックを経由するようになります
フック内で event.stopPropagation()
が実行されるため、イベントの伝搬が止まります
まとめ
フックを経由することで自由に JavaScript の処理を挟むことができました
他にも様々なことに応用できそうですね