14

更新:( 要約、フィドル、報奨金)

この質問はあまり注目されていないので、私はそれに少し担当者を費やすつもりです. 私は、回答と質問の両方で過度に冗長になる傾向があることを知っています。それが、私が先に進んでこの fiddleを設定した理由です。これは、私の見解では、バブリングchangeイベントに近づくために現在使用しなければならない種類のコードの適切な表現です。私が解決しようとしているいくつかの問題:

  1. フォーカスを失っていない限り、pseudo-changeイベントは select 要素では発生しません。場合によっては、新しい値を選択したときにクライアントをリダイレクトする必要があります。どうすればこれを達成できますか?
  2. ハンドラは、ラベルがクリックされたときと、チェックボックス自体がクリックされたときの両方で呼び出されます。それ自体はあなたが期待することですが、イベントのバブリングにより、どの要素がクリックされたかを判断することは (AFAIK) 不可能です。realTargetIE のイベント オブジェクトにはプロパティがありません。
  3. ラベルをクリックしてIEのチェックボックスの状態を変更するcheckedと、すべて問題ありませんが(厄介な回避策が必要ですが)、チェックボックスを直接クリックするとハンドラーが呼び出されますが、チェックされた状態は変更されません。 2回目。その後、値は変更されますが、ハンドラーは呼び出されません。
  4. 別のタブに切り替えて再び戻ると、ハンドラーが複数回呼び出されます。チェックされた状態が実際に変化した場合は 3 回、チェック ボックスを 1 回だけ直接クリックした場合は 2 回。

上記の問題の 1 つまたは複数を解決するのに役立つ情報をいただければ幸いです。jQueryタグを追加することを忘れていませんでした。純粋な JS が好きなので、純粋な JS の回答を探しています。


250 をはるかに超える選択要素と 20 ~ 30 のチェックボックスを含む Web ページがあります。また、ユーザーの行動を追跡し、適切な行動を取らなければなりません。したがって、何百ものリスナーを追加するのではなく、変更イベントを委任することは非常に自然なことです。

もちろん、IE - 会社のポリシー: IE8 をサポートする必要がある - は、必要なときに onchange イベントを発生させません。だから私は onchange イベントを偽造しようとしています。これまでのところ、私を本当に悩ませている1つのことを除けば、かなりうまく機能しています。イベントを登録するためにandを
使用しています。場合によっては、ユーザーが select 要素から新しい値を選択すると、スクリプトはすぐに応答する必要があります。ただし、select がフォーカスを失っていない限り、これは発生しません。onfocusinonfocusout

これが私がこれまでに思いついたものです:

i = document.getElementById('content');
if (!i.addEventListener)
{
    i.attachEvent('onfocusin',(function(self)
    {
        return function(e)
        {
            e = e || window.event;
            var target = e.target || e.srcElement;
            switch (target.tagName.toLowerCase())
            {
                case 'input':
                    if (target.getAttribute('type') !== 'checkbox')
                    {
                        return true;
                    }
                    return changeDelegator.apply(self,[e]);//(as is
                case 'select':
                    self.attachEvent('onfocusout',(function(self,current)
                    {
                        return function(e)
                        {
                            e = e || window.event;
                            var target = e.target || e.srcElement;
                            if (target !== current)
                            {
                                return;
                            }
                            self.detachEvent('onfocusout',arguments.callee);//(remove closure
                            return changeDelegator.apply(self,[e]);
                        }
                    })(self,target));
                default: return;
            }
        }
    })(i));//(fake change event, buggy
}
else
{//(to put things into perspective: all major browsers:
    i.addEventListener('change',changeDelegator,false);
}

onfocusinイベントにバインドされたハンドラー内に別のイベントリスナーをアタッチしようとしましonclickた。onfocusoutATM にフォーカスがある selectのイベントを発生させました。唯一の問題は、ユーザーの 99.9% が選択をクリックするため、focusinイベントも onclick を発生させることです。

それを回避するために、クロージャーを作成し、現在のフォーカス中の選択とその元の値を引数として渡しました。ただし、一部のユーザーキーボードを使用し、これらのユーザーは多くの場合、値を変更せずに次の選択ボックスに移動します。新しいonclickリスナーをバインドするたびに...すべてをチェックe.typeして個別に処理するよりも簡単な方法があると思います。
例として: 追加のonclickリスナーを含むコード: すべてのコードは最初のスニペットと同じなので、case 'select':ブロックのみを貼り付けています

                case 'select':
                    self.attachEvent('onfocusout',(function(self,current)
                    {
                        return function(e)
                        {
                            e = e || window.event;//(IE...
                            var target = e.target || e.srcElement;
                            if (!(target === current))
                            {
                                return;
                            }
                            self.detachEvent('onfocusout',arguments.callee);//(remove closure
                            return changeDelegator.apply(self,[e]);
                        };
                    })(self,target));
                    self.attachEvent('onclick',(function(self,current,oldVal)
                    {
                        return function(e)
                        {
                            e = e || window.event;
                            var target = e.target || e.srcElement;
                            if (target.tagName.toLowerCase() === 'option')
                            {
                                while(target.tagName.toLowerCase() !== 'select')
                                {
                                    target = target.parentNode;
                                }
                            }
                            if (target !== current)
                            {//focus lost, onfocusout triggered anyway:
                                self.detachEvent('onclick',arguments.callee);
                                return;
                            }
                            var val = target.options[target.selectedIndex].innerHTML;//works best in all browsers
                            if (oldVal !== target.options[target.selectedIndex].innerHTML)
                            {
                                self.detachEvent('onclick',arguments.callee);
                                return target.fireEvent('onfocusout');
                            }
                        };
                    })(self,target,target.options[target.selectedIndex].innerHTML));
                default: return;
4

2 に答える 2

3

要素ごとに 1 つの多くのリスナーではなく、フォーム全体で 1 つのイベント リスナーのみを使用する方がよいことに同意しますが、決定のコストと利点を評価する必要があります。1 つのリスナーの利点は、メモリ フットプリントの削減です。欠点は、複雑なコード トリックを実行して、1 つのブラウザーのイベントの不適切な実装を回避する必要があることです。これにより、実行時間が長くなり、イベント タイプが誤用され、イベント リスナーの登録と登録解除が繰り返され、一部のユーザーが混乱する可能性があります。

利点に戻ると、すべての要素のリスナーとして同じ関数をアタッチするだけであれば、メモリ フットプリントはそれほど大きくありません。そしてメモリは、現在のコンピュータに欠けているものではありません。

これで質問の答えが得られない場合でも、これを機能させようとするのをやめて、代わりに各フォーム要素にリスナーをアタッチすることをお勧めします。

ポイントに少し対処するには:

  1. よろしいですか?Microsoft のドキュメントの基本的な例を試してみましonchangeたが、ドロップダウンからオプションをクリックすると、IE7 と IE8 の両方がリスナーを起動します。上/下キーで選択を変更するときにも起動します。

  2. ラベルのイベントを影響を受けるチェックボックスに簡単に接続できないのは事実ですが、なぜそれをしたいのでしょうか? いずれにせよ、changeイベントはチェックボックスでトリガーされ、そのチェックボックスのみを気にする必要があります。ただし、ラベルのイベントからチェックボックスに移動する必要がある場合は、手動で行うことができます。イベントがラベルをターゲットにしている場合、そのラベルがなんらかの形で入力に関連していることがわかります。inputラベルの使用方法に応じて、内にネストされた要素を選択するか、 の属性でlabel見つかった ID を持つ要素を取得できます。forlabel

  3. チェックボックスが変更時に以前の値を返すバグを修正する 1 つの方法は、チェックボックスでthis.blur()onfocusイベントを実行することです。

  4. 「ハンドラー」と言うときは、onfocusinイベント ハンドラー、またはchangeDelegator? focusinタブを再アクティブ化するときにイベントが発生するのは正常です。イベントが複数回発生する理由がよくわからないので、推測しているだけです。1 つは、アクティブな入力が受け取るフォーカスである可能性があります。2 つ目は、ドキュメント自体が受け取るフォーカスです。3 番目の呼び出しが発生する理由がわかりません。「チェックされた状態が実際に変化した場合、[...]チェックボックスを1回だけ直接クリックした場合」の意味がわかりません。

于 2012-08-17T11:13:32.687 に答える
3

さて、私はそれに別のクラックがあり、かなりまともなアプローチを思いつきました(仕事でそれはうまくいきました-私が書いたコードを複製しようとしましたが、数回ビールを飲んだ後、いくつかのエラーが含まれている可能性がありますが、精神は変わらない

window.attachEvent('onload',function ieLoad()
{
    var mainDiv = document.getElementById('main');//main div, from here events will be delegated
    var checks = mainDiv.getElementsByTagName('input');//node list of all inputs
    var checkStates = {};
    for (var i=0;i<checks.length;i++)
    {
        if (checks[i].type === 'checkbox')
        {//get their checked states on load, this object serves as a reference
            checkStates[checks[i].id] = checks[i].checked;
        }
    }
    mainDiv.attachEvent('onfocusin',(function(initState)
    {//initState holds a reference to the checkStates object
        return function(e)
        {
            e = e || window.event;
            var target = e.target || e.srcElement;
            //id of checkboxes used as key, so no checking for tagName or type required
            if (!initState.hasOwnProperty(target.id) || target.checked === initState[target.id])
            {//don't call method if checkstate is unchanged, either. I'll explain in a minute
                return e;
            }
            initState[target.id] = target.checked;//set new checked-state
            changeDelegator.apply(target,[e]);//delegate
        };
    })(checkStates));
    window.detachEvent('onload',ieLoad);//avoid mem-leak with onload handler!
});

場合によっては、ラジオとチェックボックスで focusin イベントが 2 回発生することがわかりました。すべてのチェックボックスの実際のチェック状態を保持するオブジェクトを使用すると、個々のハンドラーよりもコストがかからず、要素の値が変更された後にのみイベントを委任できます。

changeDelegator関数は必要な場合にのみ呼び出されますが、ここに投稿した anon 関数は、必要以上呼び出されますが、このアプローチは個々のハンドラー テイクよりもパフォーマンスが優れています。

選択を省略しましたが、それらも機能しました (同様に、私のコードの完全版ではクロージャーに 2 つのオブジェクトがあり、それを作成したので、ID にフラグを立て、必要に応じてぼかしイベントを発生させ、クライアントはリダイレクトされます)。
実行の終わりに、私はいくつかの新しいトリックを学びましたが、この演習から得られる主なことは、IEと呼ばれるものの恐ろしい、ザラザラしたゴーレムに対するさらに深い憎しみです...しかし、他の誰かがIE で変更イベントを委任したいと思うかもしれませんが、それが (ほぼ) 可能であることを知っておいてください。

于 2012-08-20T20:31:34.990 に答える