1

ドロップダウン メニュー付きのボタン用の単純なクロス ブラウザ プラグインを作成しようとしています。ユーザーがそのようなボタンをクリックすると、その下にメニューが表示され、さまざまなオプションが表示され、ユーザーはその中からオプションを選択するか、閉じることができます。

私が達成しようとしていることを例示する 3 つのボタンを持つ単純な JSFiddleを作成しました。私の JSFiddle コードは、以下のコードから除外した追加のイベント ログを実行しますが、JSFiddle を実行すると、発生したイベントをログに記録していることは明らかです。

これは私のHTMLです:

コードを実装する方法では、フォーカス可能なドロップダウン メニューが必要なためtabindex、コンテナーの属性が必要です。

<div class="dropdown">
  <a href="#" class="dropdown-toggle">Open sesame</a>
  <ul class="dropdown-menu" tabindex="0">
    <li><a href="#">Some option</a></li>
    <li><a href="#">Option with longer text</a></li>
  </ul>
</div>

私のスクリプト:

// menu opening and closing
$(".dropdown-toggle").mousedown(function(evt) {
  evt.preventDefault();
  var c = $(this).closest(".dropdown").toggleClass("open");
  c.hasClass("open") && c.find(".dropdown-menu")[0].focus();
});

// menu closing when clicking anywhere
$(".dropdown-menu").focusout(function(evt) {
  evt.stopPropagation();
  $(this).closest(".dropdown").removeClass("open");
})

メニューの表示はCSSで行います。ご覧のとおり、コンテナーに CSS クラスを設定することはほとんどなく、CSS はクラスopenがコンテナーに設定されている場合に自動可視性を提供します。

意図された動作

これは正しい方法です。

  1. ユーザーがボタンをクリックすると、メニューが表示されます
  2. 同じボタンをクリックすると、メニューが閉じます
  3. メニューオプションをクリックすると、オプションのクリックが発生する必要があります(オプションでメニューを開いたままにします)
  4. メニューが開いている場合は、他の場所をクリックするとメニューが閉じます。

ブラウザの問題

ブラウザが異なれば、イベントの発生方法も異なり、過度に発生するようです。イベントの伝播 (バブリング) とそのシーケンスにより、上位のステップが期待どおりに実行されなくなります。Chrome は過剰なイベントを発生させないようです。

Chrome
Chrome は期待どおりに動作するようです。4 つのステップはすべて正常に実行されます。メニュー内のリンクをクリックすると、フォーカスされたコンテナー (メニュー自体) 内のリンクとしてfocusoutが発生しません。

Firefox と IE9手順 #1、#2、および #4 は期待どおりに機能しているようですが、メニュー オプションのクリックを検出して実行する前にfocusout
が最初に起動してメニューを閉じる ため、#3 は失敗します。

IE8 と IE7
を持っている人なら誰でもテストして、上位のステップのどれが機能し、どれが失敗するかを教えてくれます。私はテストしていませんが、本当に知りたいです。

質問

このスクリプトの主な問題は、focusoutイベントが時期尚早に頻繁に発生することです。メニューオプションからメニュー自体に伝播されないため、ぼかしイベントを使用できません。

重要- クリック ハンドラーをバインドする -クリックイベントを自分のにdocumentバインドできることはわかっていますが、この通常の方法を使用することはできません:そのようなコントロールがクリックされたときに閉じません。2.アプリケーションは 内で実行されているため、その外側をクリックしてもメニューが開いたままになります。document

iframe

クロスブラウザでこれらのイベントを操作したい人はいますか?

4

2 に答える 2

0

focusoutドキュメントでクリック ハンドラーを使用して、コードを置き換えることができます。必要な動作が正確にはわかりませんが、これを試してください:

$(document).click(function(e){
  var $tgt=$(e.target)
  if( !$tgt.closest('.container').length){
   log('non menu el clicked')

  }else{
    /* close other open menus when a new one clicked*/
    $tgt.closest('.container').siblings().removeClass('open')
  }

})

デモhttp://jsfiddle.net/zMdxw/5/

これを改良して、メニューが開いているときにのみドキュメント クリック ハンドラを追加し、すべてのメニューが閉じているときに削除することができます。

于 2013-01-13T01:41:22.977 に答える
0

クロスブラウザ ソリューション

私が思いついた解決策は、クロスブラウザーであり、Chrome、Firefox、および IE7+ で動作します。追加のイベントを処理する必要があり、それがmousedownドロップダウン メニューです。ドロップダウン メニューのオプションをクリックすると、focusoutユーザーがフォーカスされている同じ要素内をクリックした場合でも、通常、IE および FF でイベントが発生します。そのため、nextfocusoutを無視してメニューを閉じないように設定しています。

Chrome はfocusoutメニュー オプションのクリックを発生させないため、十分な時間が経過したら手動で閉じることを再度有効にすることで、これを処理する必要があります。私は 100 ミリ秒に設定しましたが、次のfocusoutハンドラーが実行されるまで遅延するだけでよいため、はるかに短くすることができます。10msでも十分なようです。イベント ハンドラーが実行を開始する前にすべてのイベント ハンドラーがブラウザーによってキューに入れられている場合は、さらに少なくなる可能性があります。その場合、値は 0 で十分です。しかし、安全のために 100ms のままにしました。

これは、期待されることを行うコードです。

// toggle dropdown menu display
$(".dropdown-toggle").mousedown(function(evt) {
  evt.preventDefault();
  log("Menu toggle");

  var dd = $(this).parent().toggleClass("open");

  // only focus it when visible
  dd.hasClass("open") && dd.children(".dropdown-menu")[0].focus();
});

// dropdown closing on focusout    
$(".dropdown-menu").focusout(function(evt) {
  log("Menu focus out");
  var m = $(this);

  // check that closing is not cancelled this time
  m.data("cancel-close") === true && m.removeData("cancel-close").length || m.parent().removeClass("open");
});

// cancel dropdown closing when user clicks a menu option
$(".dropdown-menu").mousedown(function(evt) {
  log("Cancel next focusout");
  var m = $(this);

  // cancel next focusout event
  m.data("cancel-close", true);

  // reenable closing for browsers that don't focusout ie. Chrome
  window.setTimeout((function(context) {
    return function() {
      log("Focusout is reenabled.");
      context.removeData("cancel-close");
    };
  })(m), 100);
});
于 2013-01-14T15:59:22.260 に答える