10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2023

Day 22

Phoenix LiveView でクリックイベントの伝搬を止める( stopPropagation )

Last updated at Posted at 2023-12-25

はじめに

Phoenix LiveView で Web ページを作っているとき、クリック時のイベント伝搬を止めたいときがあります

具体的に言うと、例えばモーダルとフラッシュ(通知)が表示されているときです

スクリーンショット 2023-12-25 16.46.22.png

mix phx.gen.live で生成される通常のページの場合、モーダルが閉じてフラッシュが表示されるため、この状態にはなりません

フラッシュ表示後もモーダル内で続けて操作するようなケースを想定しています

通常、モーダルの外側をクリックするとモーダルが閉じます

また、フラッシュはフラッシュ自体をクリックすることで閉じます

モダールとフラッシュが両方表示されているとき、フラッシュを閉じるとモーダルまで一緒に閉じてしまいます

flash.gif

これはフラッシュをクリックしたイベントが、その後ろにある「モーダルの外側」にまで伝わってしまっているためです

フラッシュだけ閉じたい場合、クリックイベントをフラッシュより後ろに伝わらないよう、イベントの伝搬を止める必要があります

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-clickdata-flash 属性に変更します

これにより、今までクリック時に直接実行していた処理がフックを経由するようになります

フック内で event.stopPropagation() が実行されるため、イベントの伝搬が止まります

flash_stop.gif

まとめ

フックを経由することで自由に JavaScript  の処理を挟むことができました

他にも様々なことに応用できそうですね

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?