nanisore oishisou

Webエンジニアあるまさんのゆるふわ奮闘記。

Vue.jsでmouseoverとclickイベントの両方にハンドラーをバインドするとAndroidで不具合

こんばんわー!
金曜日に↓の続きを書こうと思ったんだけど、↑ってことがあっていろいろ調べていたらブログを書く時間がなくなったんで、また近々パート2は書こうと思うます。

arm4.hatenablog.com

そもそもclickイベントとmouseoverイベントの両方を同じボタンにくっつけるというのがどうかと思うという意見もあると思うが、
clickしたら速攻でモーダルが表示されて、しばらくmouseoverしてたらモーダルが表示されたい、という要件もあると思う。

そういうときに、普通だとclickとmouseoverのイベントの両方にハンドラーをバインドするわけだが、Vue.jsで書くとこんな感じになる。

<button
      class="btn btn-info"
      @click="showModal(true)"
      @mouseover="showModal()"
      @mouseleave="clearTimer"
>
      clickとmouseover同時発生するボタン
</button>
.
.
.
    showModal: function(clicked = false) {
        if(this.isTouchDevice || clicked){
          $('#wrapper').append('<div>クリックしたね!</div>');
          this.$root.$emit('open-modal');
          this.$root.showModal = true;
         } else {             
           this.setTimer
             = setTimeout(function(){
             $('#wrapper').append('<div>マウスオーバーしたね!</div>');
             this.$root.$emit('open-modal');
             this.$root.showModal = true;
           }.bind(this), 600); 
         }
    },
    },

こんな感じで、同じようにモーダルを表示させるわけだから、clickしたら速攻表示で、それ以外の時はタイマーにセットするという感じで最初は書いたわけです。

そこに、スマホの場合はclickとか関係なく全部速攻表示にしてねという感じで条件文に追加したというのが上記のコードです。

ご覧のとおり、なんでそんなことするの?という非常に分かりづらいコードになってます。

メソッド分ければいいじゃん!!!!!

そもそも、最初にあったメソッドの中でなんやかんやしだしたことが問題ですよね。

まあ、コードがイケてないというのは置いておいて、挙動自体は要件に沿ったものが実装できています。

が、

Androidでモーダルが開かないという不具合が報告されました。

実機とエミュレータで確認してみたところ、たしかにクリックしてもモーダルが開きません。

やっぱりmouseoverとclickの同時バインドが原因だなというのがピンと来たので、検証してみたところイベント自体は二重発生はしていませんでした。

どうやらmouseoverとclickの両方にハンドラーがバインドしてあるとバグるようです。

ということで、いろいろ試してみた結果、Vue.jsでイベントにハンドラーをバインドするのはテンプレートのHTMLタグ内でやらないといけないんですが、このハンドラーを記述する部分に三項演算子が使えるので、ここで特定の条件の時は、ハンドラーをnullにすることでmouseoverイベントにハンドラーがアタッチされていない状態を作り出すことができました。

<button
        class="btn btn-success"
        @click="clicked()"
        @mouseover="!isMobile ? mouseovered() : null"
        @mouseleave="!isMobile ? clearTimer : null"
>
        スマホならclickしか発生しないボタン
</button>
.
.
.
    clicked: function() {
      $('#wrapper').append('<div>クリックしたね!</div>');
      this.$root.$emit('open-modal');
      this.$root.showModal = true;
    },
    mouseovered: function() {
        this.setTimer
           = setTimeout(function(){
              $('#wrapper').append('<div>マウスオーバーしたね!</div>');
               this.$root.$emit('open-modal');
               this.$root.showModal = true;
             }.bind(this), 600);  
    },

こうやって見ると、最初からこうすればよかったんじゃない?

そうじゃない?

という気もする。

動くサンプルを以下に置いてみたので、もっといい方法が思いついた人はどしどしご応募お待ちしております。

JS Bin on jsbin.com

http://jsbin.com/gisahivoda/edit?html,js,output

機種依存の不具合を直すのって大変なことが多いですが、いつも発想の転換を求められるので頭の体操になったり、逆にコードが洗練されたりしますよね。

という、ちょっと今回は真面目な話で締めくくりましたYO★