51326


オーバーレイ（画面を薄暗くする、擬似的なモーダルダイアログを作るときなどに使うやつ）を表示しているとき、オーバーレイよりも下にある要素へのマウス操作は無効化できますが、キーボード操作が無効化できません。  
例えばTabキーでフォーカス移動して、Enterでボタンを押せたり。

あらかじめ画面上の操作可能な要素を洗い出して1つずつdisableしたり、keydownイベントハンドラの冒頭に条件分岐を書いたり、といった泥臭いやり方なら浮かびますが、もっとシンプルに実装できないものでしょうか。

オーバーレイ表示中は、操作可能な要素の方が少ないので、セレクタ指定などで「これら以外はキー入力を無視する」といった実装方法が取れるとだいぶ楽だと思っています。  
例えば`active-on-overlay`というクラスのついた要素の子孫だけが操作可能だとして、

[code]

    if ($(this).closest(".active-on-overlay").length !== 0) { ... }
    
[/code]

とか・・・  
jQueryの`on`を`document`に対して設定して全要素のイベントをうまく処理したりとか、何とかならないかと漠然とした考えもありますが、なかなか思いつきません。  
すでに設定済みのイベントハンドラがあれば、それも止める必要もありますし・・・

ガチガチに完全に防ぐつもりは無く、誤操作の範囲を防止できればひとまず十分と考えていますが、何か良い方法は無いでしょうか。


CC BY-SA 4.0
2018-12-17T02:34:32.743
51278
2018-12-18T06:19:53.297
8078
question
javascript
オーバーレイの下にある要素でのキーイベントを無効化したい


通常、フォーカスがない限りはキーボードイベントは処理されないので、

  * オーバーレイを表示したときにオーバーレイ外にフォーカスがないことを保証する
  * オーバーレイを表示しているときにオーバーレイ外にフォーカスが移ったら、なんとかする

でうまくいくのではないかと思います。

下記コードサンプルで、`openDialog()`が1番目を、`focus`イベントリスナが2番目を処理しています。

`<dialog>`を使えば何も心配いらないのですけど、現状で使うのは厳しいですね。

[code]

    let showingDialog = false;
    function openDialog() {
      document.querySelector('.backdrop').style.display = 'block';
      document.querySelector('.active-on-overlay').style.display = 'block';
      document.body.style.overflow = 'hidden';
      showingDialog = true;
      // ダイアログ内の要素にフォーカスを移しておく。
      document.querySelector('.active-on-overlay input').focus();
    }
    function closeDialog() {
      document.querySelector('.backdrop').style.display = 'none';
      document.querySelector('.active-on-overlay').style.display = 'none';
      document.body.style.overflow = 'auto';
      showingDialog = false;
    }
    document.addEventListener('focus', e => {
      // ダイアログ内でTABキーなどを押して外の要素にフォーカスが移ったら、
      // 強制的にダイアログ内にフォーカスを戻す。
      if (showingDialog && !e.target.closest('.active-on-overlay'))
        document.querySelector('.active-on-overlay input').focus();
    }, true);
[/code]

[code]

    .active-on-overlay {
      display: none;
      position: absolute;
      width: 30%;
      height: 20%;
      top: 50%;
      left: 50%;
      transform: translateY(-50%) translateX(-50%);
      z-index: 2;
      background-color: #eee;
      box-shadow: 4px 4px 4px gray;
    }
    .backdrop {
      display: none;
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 1;
      background-color: rgba(0, 0, 0, 0.5);
    }
[/code]

[code]

    <input>
    <dialog>
      <p>&lt;dialog>デス!</p>
      <input type=button value=Close onclick="document.querySelector('dialog').close();">
    </dialog>
    <input>
    <br>
    <button type=button onclick="document.querySelector('dialog').showModal();">
    標準ダイアログを開く (2018年現在、Chromeのみ対応)</button>
    
    <div class="backdrop"></div>
    <div class="active-on-overlay">
      <p>自家製ダイアログです。</p>
      <input type=button value=Close onclick="closeDialog()">
    </div>
    <br>
    <button type=button onclick="openDialog()">自家製ダイアログを開く</button>
    
    <div style="height:9999px"></div>
[/code]


CC BY-SA 4.0
2018-12-18T06:19:53.297
51326
2018-12-18T06:19:53.297
3475
51278
answer