(Vue.jsと書いたけど、そんなにVue.jsは関係ない)
業務でVue.jsを用いてウェブアプリを作っているのですが、そのアプリのうち、あるボタンをクリック(タップ)ではなく、触ったとき、つまりマウスのボタンを押し下げた時やタッチデバイスに触れた時にイベントが発生してほしいという要望を受けました。
それを聞いた瞬間、「Pointer Events」が使えると思いました。過去にここのブログでも紹介しています(現時点でIEのみ実装されてるPointer Eventsが超便利 | while(isプログラマ))。過去にはIEでしか実装されてなかったそろそろ他のブラウザでも実装されてるのではないかと思い、ためしにpointerdownで実装してみました(参考:pointerdown – Event reference | MDN)。試しにChromeで動かしてみると、想定通りに動きました! やったー!
<template> <span class="startBtn" v-on:pointerdown="start">PointerDown</span> </template>
と思ったのもつかの間、「できました!」と上司に伝えたところ、「iPhoneで動かないんだけど」とのこと。なんと、PointerEventsはSafariには実装されてませんでした(泣)。
参考:Pointer events:Can I use… Support tables for HTML5, CSS3, etc
仕方がないので、mousedownとtouchstartで書き直すことに。まあ、一つ記述が増えるだけだしいいか。多分、マウスボタンを押すとmousedown、タッチパネルでタッチするとtouchstartのイベントが発生するだろうしと思って、下記のような感じで書きました(イベント内にはconsole.logの記述しかしてませんが、実際には通信処理を行う記述でした)。
<template> <span class="startBtn" v-on:mousedown="start" v-on:touchstart="start">MouseDown OR TouchStart</span> </template> <script> export default { name: 'MouseTouchStart', methods:{ start(ev){ console.log(ev.type); } } } </script> <style> .startBtn{ display:inline-block; width:250px; height:40px; line-height:40px; border:1px solid #aaaaaa; background-color: #00A; cursor: pointer; color:white; font-weight: bold; } .startBtn:active{ background-color:#33A; } </style>
うんうん。確かに、マウスでもタッチパネルでも反応しますし、押すとボタンの色も変わってくれます。これでいいかと思って上司に報告。しちゃダメでした。「二回発生しているように思うんだけど」と……。いやいやそんな馬鹿な。実はタッチしたときに2か所で反応しちゃってるだけじゃないの? なんて思いましたが、試しにChromeのDeveloper Toolのエミュレータで見てみるとtouchstartもmousedownのイベントも発生しました(しかも、mousedownのイベントが発生しない時もある)。
調べてみると、確かにタッチデバイスでもマウスイベントが起こるものらしい。preventDefault()でデフォルトイベントをキャンセルするとmousedownは発生しないらしい(参考:TouchEvent と MouseEvent の両方をサポートする – ウェブデベロッパーガイド | MDN)。仕方がないので、Vue.jsのpreventイベント修飾子を使って実装することに。
<template> <span class="startBtn" v-on:mousedown.prevent="start" v-on:touchstart.prevent="start">MouseDown OR TouchStart</span> </template>
この実装で何回か試してみて確かにイベントが一回しか実行されないようになりました。これで一安心。と思ったのもつかの間。「Androidだと押した感じがしないのだけど」と。いやいや、active疑似クラスでボタンの色は変えてるし……。と思って自分のAndroidで試してみるとタップしてもボタンの色が変わりませんでした……。preventDefault()を行うことで、active疑似クラスのスタイル指定が反応されなくなってしまったのでした。仕方がないので、自分でアクティブ化用のクラスを追加し、ポインターがはずれるとクラスを外すことに。
<template> <span class="startBtn" v-on:mousedown.prevent="start" v-on:touchstart.prevent="start" v-on:mouseup="isActive=false" v-on:mouseout="isActive=false" v-on:touchend="isActive=false" v-bind:class="{active: isActive}">MouseDown OR TouchStart</span> </template> <script> export default { name: 'MouseTouchStart', data(){ return { isActive: false } }, methods:{ start(ev){ console.log(ev.type); this.isActive = true; } } } </script> <style> .startBtn{ display:inline-block; width:250px; height:40px; line-height:40px; border:1px solid #aaaaaa; background-color: #00A; cursor: pointer; color:white; font-weight: bold; } .startBtn.active{ background-color:#33A; } </style>
早く、PointerEventsが使えるようになってくれないだろうか。
2019/02/24追記:サーバにもアップしました。
MousedownとTouchstart
コメント
問題の本質は、仕事としてやっているのなら、動作確認するのが当たり前ということ。
たったそれだけの事が出来ない素人が給料をもらってはいけません。