JavaScript
jQuery
edge
Windows10

タブレット Windows10 Edgeのドロップダウンメニュー(:hover式)が動かなくて困った話

ドロップダウンメニュー(:hover式)を利用したグローバルメニューがあるサイトの構築を行った際に、Windows10 + Edge のタブレット端末で困った話です。

はじめに

ドロップダウンメニュー(:hover式)とは

Webシステムにおいて、ドロップダウンのような隠し表示は、様々な技術で実現されていて、マウスカーソルが見出し要素の上に来たらリストテーブルや説明を表示させるようにCSSの:hoverで制御することが多い( 参考
その他に、JavaScriptでClickやMouseOverイベントを介して表示したりする場合も(参考)。

タブレット端末の操作は基本「ゆび」

デスクトップ全盛の時期に、Webシステムを作っていた私は、
どうしても忘れるのが、タブレット端末の操作の基本は「ゆび」であること(もしくは、ペンなど)。
Bluetoothでマウスを接続して操作している人もいるかもしれないが、持ち運びの際には、使いたくても使えない。

メニューとか説明をわかりやすくしたつもりでも、タブレットやスマホでは表示されなくて不要の長物になってしまうことを後で気付くことが多い。

Webシステムは、社内でしか利用しないガチガチの業務アプリ以外、クリック操作(タップ操作)で基本的に動作するようにした方がいい。

困った事象

結論から書くと、タブレットのWindows10 Edge では、
タップイベントでサブメニューが表示されなかった。

ポータルサイトに利用したドロップダウンメニューのテンプレートには、
以下のスタイルがもともと定義されていた。

.category-nav .mega-menu ul {
  margin: 0;
  padding: 0;
}
.category-nav .mega-menu ul li {
  background: #ffffff;
  display: inline-block;
  float: left;
  text-align: center;
  width: 12.5%;
  position: static;
}
.category-nav .mega-menu ul li a {
  position: relative;
  display: block;
  padding: 25px 0px;
  font-size: 14px;
  line-height: 20px;
  text-decoration: none;
  color: #222222;
  font-weight: normal;

  -webkit-transition: all 0.4s;
  -o-transition: all 0.4s;
  transition: all 0.4s;
}
.category-nav .mega-menu ul li > a {
  border-right: 1px solid #EAEAEA;
}
.category-nav .mega-menu ul li a:hover,
.category-nav .mega-menu ul li a:focus,
.category-nav .mega-menu ul li a:active {
  background: #2a2f33;
  color: #ffffff;
}
.category-nav .mega-menu ul li:hover .menu-wrapper,
.category-nav .mega-menu ul li:focus .menu-wrapper {
  visibility: visible;
  opacity: 1;
  filter: alpha(opacity=100);
  display: block;
}
.category-nav .mega-menu ul li:last-child {border-right: 0px;}
.menu-wrapper {
  text-align: left;
  position: absolute;
  visibility: hidden;
  display: none;
  z-index: 1;
  height: auto;
  left: 0px;
  top: 73px;
  width: 100%;
  margin: 0;
  padding: 0;
  background: #2a2f33;
  border: 0px;
  -webkit-transition: all 0.4s;
  -o-transition: all 0.4s;
  transition: all 0.4s;
}

ul/liでドロップダウンメニュー(.category-nav)を定義しており、
li要素にマウスが移動して被ると、:hover定義で、隠れていたサブメニューの要素(.menu-wrapper)が表示されるというもの。

HTMLは以下の通り。

<div class="category-nav" style="display: block;">
  <div id="mega-menu" class="mega-menu">
    <ul>
      <!-- ___Start Category Nav Life Style___ -->
      <li id="life-style" class="mega-menu-li life-style"><a href="#0">Life Style</a>
        <ul>
          <li>
            <!-- ___Start Menu Wrapper___ -->
            <div class="menu-wrapper">
              <div class="post-tab container-fluid no-padding">
                <div role="tabpanel">
                  <div class="row margin-zero">
                    <div class="col-md-3 col-sm-3 no-padding">
                      <div class="submenu-lists">
                        <h4>Style News <i class="fa fa-leaf"></i></h4>
                        <!-- ___Nav Tabs___ -->
                        <div class="nav nav-tabs" role="tablist">
                          <div class="list active" role="presentation"><a href="#beauty-care" aria-controls="beauty-care" role="tab" data-toggle="tab">Beauty Care</a></div>

                          <div class="list" role="presentation"><a href="#hair-style" aria-controls="hair-style" role="tab" data-toggle="tab">Hair Style</a></div>

                          <div class="list" role="presentation"><a href="#spa" aria-controls="spa" role="tab" data-toggle="tab">Spa</a></div>

                          <div class="list" role="presentation"><a href="#cosmetics" aria-controls="cosmetics" role="tab" data-toggle="tab">Cosmetics</a></div>

                          <div class="list" role="presentation"><a href="#facial" aria-controls="facial" role="tab" data-toggle="tab">Facial</a></div>

                          <div class="list" role="presentation"><a href="#skin" aria-controls="skin" role="tab" data-toggle="tab">Skin</a></div>
                        </div><!-- End Nav Tab -->
                      </div><!-- End Sub Menu List -->
                    </div><!-- End Column -->

                    <div class="col-md-9 col-sm-9">
                      <!-- ___Tab Content___ -->
                      <div class="tab-content">

                        <!-- ___Tab Pane___ -->

                          ...
                          ... (ここに記載するサブサブ情報表示は今回不要なので省略)
                          ...

                        <!-- End Tab Page -->
                      </div>
                      <!-- End Tab Content-->
                    </div><!-- End Column -->
                  </div><!-- End Row -->
                </div><!-- End Tab Panel -->
              </div><!-- End Post Tab -->
            </div><!-- End Menu Wrapper -->
          </li>
        </ul>
      </li>
      <!-- End Category Nav Life Style -->

      <!-- ___Start Category Nav Travel___ -->
      <li id="travel" class="mega-menu-li travel"><a href="#0">Travel</a>
        <ul>
          <li>
            <!-- ___Start Menu Wrapper___ -->
            ....
            ...
            ... (省略)
            ..

          </li>
        </ul>
      </li>
      <!-- End Category Nav Health -->

    </ul>
  </div>
</div>

「Life Style」を表示するLIタグにマウスオーバされると、:hoverが発火されるようになっていた。

製造時、Edge以外のブラウザでは、ドロップダウン動作が、マウス操作やタップ操作で正常に発火していた。けど、動作確認で、タブレット端末のEdgeではタップ操作だけ動作しなかった。ただし、長押しでは動作した(ただし、右クリック時のメニューも表示されて痛々しい感じで)。また、ちょい長押しで一瞬表示される。

そして、マウスを繋いで確認したら何の問題もなかった。

(なんか、はらだたしい。)

解決方法

後述に説明しているが、原因はブラウザの仕様にあるっぽかったため、
しゃあなしで、昔ながらのクリックイベントを使った制御にした。
以下がjavascriptソース。

$(function() {
  var close_all_nav, open_class_name, open_nav, open_status, target_dropdown_name, target_nav_name, wrapper_class_name;
  wrapper_class_name = '.menu-wrapper';
  target_nav_name = '.category-nav .mega-menu ul li';
  target_dropdown_name = target_nav_name + ' ' + wrapper_class_name;
  open_class_name = 'open-nav';
  open_status = false;
  open_nav = function(_this) {
    var dropdown_nav;
    dropdown_nav = $(_this).find(wrapper_class_name);
    close_all_nav();
    return dropdown_nav.addClass(open_class_name);
  };
  close_all_nav = function() {
    return $(target_dropdown_name).removeClass(open_class_name);
  };

  /*
   * EdgeはデフォルトでタッチイベントがOffであるため、clickイベントを使う
   * それに伴ってその他のブラウザでも動作するように実装
   */
  $(document).on('click', target_nav_name, function() {
    open_nav(this);
    return open_status = true;
  }).on('click', 'body', function() {
    if (open_status === true) {

      /* 押されたすぐなので、何もしない */
    } else {
      close_all_nav();
    }

    /* 初期化 */
    open_status = false;
    return true;
  });
  return $(target_nav_name).hover(function() {
    return open_nav(this);
  }, function() {
    return close_all_nav();
  });
});

// ---
// generated by coffee-script 1.9.2

bodyに実装したイベントの処理は改善の余地ありなど、ツッコミどころはあるとは思う。

検討過程

:hoverスタイルが、Edgeのタップ操作で効かない

最初に、カスタマイズしていない状況で、Windows10 + Edgeの Lenovo 「Miix 2 8」で
動作検証したところ、タップしても、表示されるはずのサブメニューが出てこず、
うんともすんとも変化がなかった。
長押しをしたら、ピロっと右クリックメニューと一緒に出てきた。
冗談なのか。。。と思った。

試しに、別のマシンで、マウス操作をWindows10 + Edgeの環境で試したら何の問題もなかった。
やはり、タップ操作に何らかの原因があることがわかった。

touchstart/touchendが、Edgeのタップ操作で効かない

次に、Google先生に確認したら、タップ操作はtouchstartイベントが発火することを教えてもらったため、
イベントが発火しているどうかも含めてconsole.logを出力した。
そうしたら、ログはでず。無反応だった。

Edgeのタップ操作で使えるデフォルトのイベントは少なく、他のブラウザと比べて独自仕様。

そこで、現状で使えるイベントはなにか?を確認するべく、
jQueryで、click・hover・foucus・touchstart/endのイベントにおいて、
console.logを出力するクロージャーを作成してみたところ。

Edge以外のブラウザでは、タップしたときに以下が起動するが、

  • click
  • hoverのstart
  • focusのin
  • touchstart

Edgeの場合、以下のようになった。

  • click
  • hoverのstart
  • hoverのend
  • focusのin
  • focusのout

一応、:hoverは走っていたのに、
すぐさま、:hoverはクリアされる感じになっていた。
だから、サブメニューが表示が一瞬で消えるために目視では確認できなかった。

通常のタップでは、全くサブメニューが表示されないが、
ちょっとだけ長押しで一瞬みえて消えるのはそういった理由だろう。

上記の結果を踏まえて、:hoverのCSS定義を使った
サブメニュー表示制御は無理であると判断することにした。
JavaScriptのイベントを駆使した方法で、
ドロップダウンメニュー制御を実現させることに方針転換した。

Edgeのタッチイベントはインストール時はオフで利用出来ない

そうして、これまでの経緯で、今回はタップの操作の問題なのだから、
デスクトップ表示時の処理に影響がないようにclickイベントではなくtouchstartイベントを使いたいと考えた。

Google 先生に確認したところ、Edgeではインストール直後は、タッチイベントが無効化されているという
驚愕の記事を見つけてしまった(2018/02時点)。
そして、実際に確認したら、オフにだった。
デスクトップ版で誤作動があるんだろうか。。。まだ、理由などは調べていない。

https://stackoverflow.com/questions/33300962/touch-api-e-g-touchstart-not-working-in-ms-edge

端末に影響なくMicrosoftのブラウザ込みで問題ない方法の検討

最終的に、Edgeでタッチイベントが無効化されている以上、
利用者にその設定をお願いすることはほぼできないため、
シンプルにclickイベントを使うことにした。

clickの他にfoucusイベントなどを使った場合、開始処理と終了処理を区別する方法を
考えるよりも、表示状態を管理した方が実装に時間がかからなくて済むと考えたから。


以上、タブレットのWindows10 Edge環境で、ドロップダウンメニュー(:hover式)が動かなくて困った話でした。

対処方法がすこし場当たり的な方法であり、
EdgeやjQueryの仕様などを突っ込んで調べた結果ではないため、
ツッコミどころはあると思いますが、ひとまず今回はこのような結果になりました。

(本当は、Anglar系とか、Virtual DOMとかで書きたいけど。。。。)