おはようございます。
毎朝、4キロほどウォーキングをするようにしたら、体のあらゆる計測値が健康値に近づいている感覚が得られたので、毎日気分がいい日を過ごすことができるようになりました。
でも、睡眠クオリティは相変わらずそんなに高くないので、そのうちITを使って、ちゃんと睡眠品質を向上させようと計画していながら、仕事に勤しんでいる状態です。
そんな仕事でJavascriptを扱っていると、ほとんどのJSコードでイベント処理を書いていることに気が付きます。
そんななくてはならない「イベント操作」で、addEventListenerを利用するケースがありますが、
これまであまり意識して来なかった、第3引数を使わないとバグのもとになる事があったので、備忘録としてブログに残しておきます。
トラブルの片鱗
WEBサービスであるブラウザアプリを構築していた時に、スマートフォンでの使用を前提としていた際に、スクロールを司るのが非常に難しい事に気がつく。
PCブラウザでWEBページを見ていると、ページ全体がスクロールするの中に、単体の部分要素でスクローラブルな要素があっても違和感がありませんが、スマホでは、操作感も含めて違和感ありまくりです。
amazonのトップページのような、カルーセル機能を使った、ページ全体は縦スクロールするので、要素スクロールは横スクロールにするという方式は極めてUXに優れていますが、独自で作ったプルダウン機能などの際に、内容リストが多い場合は、どうしても部分スクロールせざるを得ません。
この場合に、全て横スクロールで統一する事はかなり厳しいため、そこは縦スクロールにすると、ページの縦スクロールと干渉してしまい、うまく動作できなくなります。
他にも、ページ内をオブジェクトをドラッグで移動するような機能をもたせた場合に置いても、ページ全体スクロールと干渉して、世にも奇妙な動作になってしまって、本当に気持ち悪くなってしまいます。
そんなときは、イベント発火中に、「event.preventDefault()」として、発動しているデフォルトイベントを停止するだけで、想定通りの動きが表現できるようになるんですが、どうもそれが動作しないことに気が付きました。
要するに以下のようなソースの場合の話です。
// ページスクロールを伴う
element.addEventListener("touchmove" , function(event){
// Drag移動処理
} , false);
// ページスクロールを停止する
element.addEventListener("touchmove" , function(event){
event.preventDefault();
// Drag移動処理
} , false);
でも、こうすると、jsコンソールに以下のようなエラーが表示されます。
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See <URL>
翻訳してみると、
[介入]ターゲットがパッシブとして扱われるため、パッシブイベントリスナー内でデフォルトを防止できません。 <URL>を参照してください
なんだかよくわかりにくいですが、「ターゲットがパッシブとして扱われるため・・・」の箇所が問題っぽいんですが、これを回避するために、第3引数を使わなければいけないようです。
パッシブモードを無効にする方法
// パッシブモード無効
element.addEventListener("touchmove" , function(event){
event.preventDefault();
// Drag移動処理
} , {passive : false});
これまで、trueとfalseしか意味を成さないと思っていたんですが、こういう使い方をするモードでもあったんですね。
どうやら、preventDefault()はパッシブモードをOFFにしないと、エラーになるという事がわかりました。
でもエラーが表示されても、動きは想定通りだったんですが、エラーは無くすのがセオリーなので、この処理は必須であると考えます。
そもそもパッシブモードって何?
active(行動的)の対義語のpassive(受動的)なのだそうですが、javascriptにおけるpassiveモードは、preventDefault機能のためのものであると考えたほうが良さそうです。
addEventListenerの第3引数のpassiveフラグも、この機能限定で扱うためのもののようです。
※ググってもこれ以外の例が出てきません・・・・
とりあえず、この件を踏まえて、イベント登録のスニペットを以下のようにすることにしました。
// [共通関数] イベントセット
var setEvent = function(target, mode, func , option ){
option = (option) ? option : false;
if (target.addEventListener){target.addEventListener(mode, func, option)}
else{target.attachEvent('on' + mode, function(){func.call(target , window.event)})}
};
この関数は、旧IEでも動作するようにattachEventも組み込んでいるので、B2B商材などを扱う際に非常に有効です。
とりあえず、本日の教訓として、「スマホのtouchイベント登録時には、passiveモードをoffにする設定が必須」という事を覚えておきましょう!!!
参考サイト
https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
0 件のコメント:
コメントを投稿