2015年2月28日

ASP.NET __doPostBack は jQuery.submit(handler) 未対応

jQuery.submit(handler) でイベントを登録しても、__doPostBack によるサブミット(ポストバック)では呼び出してくれない。
原因は、jQuery.submit(handler) は基本的にイベントリスナ(addEventListener, attachEvent)を使用し、__doPostBack はイベントリスナを考慮してないためである。

[一般的な __doPostBack の実装]

var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

DOM の onsubmit プロパティは考慮してくれているが、イベントリスナについてはノータッチである。
(ざっと調べた感じ、イベントリスナのみの呼び出しはかなり面倒・・・というかできるか怪しい)

__doPostBack を使わないようにすれば問題ないのだが、asp:LinkButton や各種コントロールのイベント(SelectedIndexChanged など)を使ってしまうと、__doPostBack の使用は回避できない。

解決手段① onsubmit プロパティを使用

__doPostBack の実装に合わせて、素直に onsubmit プロパティを使ってやれば問題ない。
ただし、複数イベントの登録が面倒になる。
(onsubmit は function を 1 つしか登録できない)

解決手段② __doPostBack 上書き

javascript は関数の上書きができるので、__doPostBack を jQuery.submit(handler) を考慮する関数で上書きする。

[__doPostBack 上書き]

if( typeof __doPostBack === 'function' && typeof theForm === 'object' ){
    var __doPostBackOriginal = __doPostBack;
    __doPostBack = function(eventTarget, eventArgument){
        if( $(theForm).triggerHandler('submit') !== false ){
            // イベントが登録されてない場合は undefined が返ってくるので厳密比較
            // イベントで false を返せば、サブミットは実行されない
            __doPostBackOriginal(eventTarget, eventArgument);
        }
    };
}

注意事項として、上記は event.preventDefault には対応してない。
event オブジェクトはクロスブラウザ対応が面倒なので・・・

また、aspx は <form runat=server> をページ内に 1 つしか定義できないため、theForm が複数あるなどの考慮は不要。

解決手段③ form.submit 上書き

上記と似ているが、今度は大元である form.submit を「サブミットボタン押下」の挙動に変更する。
form.submit では登録したイベントは発生しないが、HTMLElement.click を使ってボタン押下をシミュレートすると、イベントを発生させることができる。

[form.submit 上書き]

// 全 form 対象
[].forEach.call(document.forms, function(f){
    f.submitOriginal = f.submit;
    f.submit = function(){
        // 非表示のサブミットボタンを追加 -> 押下 -> 削除
        $('<input type="submit" name="_適当な重複しにくい名前_" style="display:none" />')
        .appendTo(f).click().remove();
    };
});

注意事項として、onsubmit プロパティを使用している場合は、__doPostBack を使うとイベントが 2 回呼ばれてしまう。
jQuery.submit() でサブミットした場合も、jQuery で登録したイベントが 2 回呼ばれてしまう。
なので、これらは使わないようにするしかない。

副作用は大きいが、form.submit でイベントが発生して欲しい場合は、便利かもしれない。
逆に、イベントが発生しないサブミットは、form.submitOriginal を使用する。

0 件のコメント:

コメントを投稿