ASP.NET Web Forms アプリには MaintainScrollPositionOnPostBack プロパティというものがあって、ポストバック前後で上下左右のスクロール位置を維持できる仕組みが用意されています。
ただし、ASP.NET 2.0, 3.0, 3.5 でブラウザに Safari, Chrome を使用した場合は動きません。理由は、先の記事「Safari は downlevel browser?」に書きましたように、Safari, Chrome のブラウザ定義に問題があるからです。(ASP.NET 4 は問題なし)
以下に、上下左右のスクロール位置を維持する仕組み、ASP.NET 2.0, 3.0, 3.5 での Safari, Chrome のブラウザ定義は何故ダメか、その対処方法を書きます。
まずどういう仕組みでスクロール位置を維持するかですが、Page で MaintainScrollPositionOnPostback が true に設定されていると、ブラウザ定義に問題がなければ以下のようなスクリプトと隠しフィールドが html ソースに含まれてレンダリングされます。(注:初期画面では隠しフィールドの value は "0" になり、下の方のインラインスクリプトで window.onload での操作を行う 2 行は含まれません)
01.
<script type=
"text/javascript"
>
02.
03.
var theForm = document.forms[
'form1'
];
04.
if
(!theForm) {
05.
theForm = document.form1;
06.
}
07.
function __doPostBack(eventTarget, eventArgument) {
08.
if
(!theForm.onsubmit || (theForm.onsubmit() !=
false
)) {
09.
theForm.__EVENTTARGET.value = eventTarget;
10.
theForm.__EVENTARGUMENT.value = eventArgument;
11.
theForm.submit();
12.
}
13.
}
14.
15.
</script>
16.
17.
18.
<script src=
"/WebResource.axd?d=Dgk..."
type=
"text/javascript"
>
19.
</script>
20.
21.
22.
<input type=
"hidden"
23.
name=
"__SCROLLPOSITIONX"
24.
id=
"__SCROLLPOSITIONX"
25.
value=
"50"
/>
26.
<input type=
"hidden"
27.
name=
"__SCROLLPOSITIONY"
28.
id=
"__SCROLLPOSITIONY"
29.
value=
"200"
/>
30.
31.
32.
<script type=
"text/javascript"
>
33.
34.
theForm.oldSubmit = theForm.submit;
35.
theForm.submit = WebForm_SaveScrollPositionSubmit;
36.
37.
theForm.oldOnSubmit = theForm.onsubmit;
38.
theForm.onsubmit = WebForm_SaveScrollPositionOnSubmit;
39.
40.
theForm.oldOnLoad = window.onload;
41.
window.onload = WebForm_RestoreScrollPosition;
42.
43.
</script>
上のコードで真中あたりにある WebResource.axd は HTTP ハンドラで、Page の埋め込みリソースとして設定されている外部スクリプトファイルを取得します。
その外部スクリプトファイルの中に、上にアップしたコードの下の方にあるインラインスクリプトで使う WebForm_SaveScrollPositionSubmit メソッド他の定義が含まれています。(興味があれば開いて中身を見てください。Visual Studio でデバッグ実行すれば開いてブレークポイントを設定したりできます)
これらのスクリプトによって、(1) ポストバックされる前の画面のスクロール位置を取得、(2) 隠しフィールドの value に画面のスクロール位置を設定してサーバーに送信、(3) サーバーから応答が戻ってきて画面が再描画されるときに隠しフィールドからポストバック前のスクロール位置を取得、(4) 画面のスクロール位置をポストバック前と同じに設定・・・という操作を行います。
しかしながら、ブラウザ定義ファイルで capability 要素 (capabilities の子要素) の supportsMaintainScrollPositionOnPostback が true に設定されてないと ASP.NET は必要なスクリプトや隠しフィールドを生成しません。
ASP.NET 2.0, 3.0, 3.5 用のブラウザ定義ファイルは以下のフォルダにあります。
C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG\Browsers
2016/1/11 時点では mozilla.browser ファイルの中に id="Safari1Plus" という browser 要素があって、Safari(かなり古いものは除く)および Chrome はその定義の適用を受けます(継承元の Default ← Mozilla ← Gecko ← Safari の定義も)。
Default のブラウザ定義で supportsMaintainScrollPositionOnPostback は false に設定されていますが、Default ← Mozilla ← Gecko ← Safari ← Safari1Plus という継承の過程でそれを true に設定するものがありません。(注:ASP.NET 4 のブラウザ定義ファイルは問題ありません)
結果、Page で MaintainScrollPositionOnPostBack プロパティを true に設定しても ASP.NET は必要なスクリプトや隠しフィールドを生成しません。
解決策は、アプリケーションルート直下に App_Browser フォルダを追加し、さらにそのフォルダに .browser ファイル(名前は任意)を追加し、その中で refID 属性を使用して既存の id="Safari1Plus" のブラウザー定義で supportsMaintainScrollPositionOnPostback が true になるように設定してやります。
具体的には、以下のコードを新たに作った .browser ファイルに記述します。これによって、Safari, Chrome でも必要なスクリプトや隠しフィールドが生成されるようになるはずです。
01.
<
browsers
>
02.
<
browser
refID
=
"Safari1Plus"
>
03.
<
capabilities
>
04.
<
capability
05.
name
=
"supportsMaintainScrollPositionOnPostback"
06.
value
=
"true"
/>
07.
</
capabilities
>
08.
</
browser
>
09.
</
browsers
>
CONFIG\Browsers フォルダの中の mozilla.browser 他の定義済みのブラウザ定義ファイルを修正するのは NG ですので注意してください。修正しても修正結果は反映されませんので。(詳しくは MSDN Blog の記事「ASP.NET の IE10 対応について」を見てください)