WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

リストボックスのスクロール位置を維持

by WebSurfer 20. November 2011 15:47

リストボックスの内のアイテムのスクロール位置をポストバック前後で維持する方法です。IE9, Firefox 8.0, Safari 5.1.1, Opera 11.52 で検証してみましたが、Opera 以外は期待通りの動きをしました。(Opera では scrollTop が取得できず、うまくいきません。他の方法を検討中です)(2011/11/21 追記: IE6 もダメでした。IE7, IE8 は OK でした)

ListBox のスクロール位置を保持

IE ではリストボックスの中の項目を選択するしないにかかわらず、ポストバックすると一番先頭に戻ってしまいます。その他のブラウザでは、リストボックスの中の項目を選択すればポストバック後に一番先頭に戻ることはありませんが、スクロール位置はポストバック前後で変わってしまいます。

今回の例では、JavaScript の scrollTop を使用して、ポストバック直前のリストボックスのスクロールバーの上端の座標を取得して隠しフィールドに保存し、ポストバックして再描画する時に隠しフィールドに保存した座標を取得してスクロール位置を設定しています。

そのサンプルコードは以下の通りです。上の画像は、このコードを実行したときのブラウザの仮面です。実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。

隠しフィールドには ASP.NET の HiddenField サーバーコントロールを使いました。これを使うと自動的に ViewState によってポストバック前後の Value が維持されます。普通の html の <input type="hidden" ... /> を使う場合は、ポストバック前後の value の維持に一工夫必要です。

リストボックスには、これも ASP.NET の ListBox サーバーコントロールを使っていますが、スクロール位置をポストバック前後で維持する仕組みに関しては、普通の html の select でも同じです。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  void SubmitBtn_Click(Object sender, EventArgs e)
  {
    string selectedItems = String.Empty;
    int count = 0;

    for (int i = 0; i < ListBox1.Items.Count; i++)
    {
      if (ListBox1.Items[i].Selected)
      {
        selectedItems += ListBox1.Items[i].Value + ", ";
        count++;
      }
    }

    if (count > 0)
    {
      selectedItems =
        selectedItems.TrimEnd(new char[] { ',', ' ' });
      Label2.Text = "You chose: " + selectedItems;
    }
    else
    {
      Label2.Text = "アイテムが選択されていません。";
    }
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    string value = HiddenField1.Value;
    Label1.Text = "Scroll Top: " + value;
  }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script src="Scripts/jquery-1.4.1.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[

  // Opera 11.52 で動かない(scrollTop が取得できない)
  function setScrollTopInHiddenField() {
    var position =
      document.getElementById("<%=ListBox1.ClientID%>").scrollTop;
    document.getElementById("<%=HiddenField1.ClientID%>").value = 
      position;
  }

  window.onload = function () {
    var position =
      document.getElementById("<%=HiddenField1.ClientID%>").value;
    if (position != "") {
      document.getElementById("<%=ListBox1.ClientID%>").scrollTop = 
        position;
    }
  }

  // jQuery を使っても、やはり Opera では動かない
//  function setScrollTopInHiddenField() {
//    var position = 
//      $("#<%=ListBox1.ClientID%>").scrollTop();
//    $("#<%=HiddenField1.ClientID%>").val(position);
//  }

//  $(document).ready(function () {
//    var position = 
//      $("#<%=HiddenField1.ClientID%>").val();
//    if (position != "") {
//      $("#<%=ListBox1.ClientID%>").scrollTop(position);
//    }
//  });

    //]]>
    </script>

</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:HiddenField ID="HiddenField1" runat="server" />
    <asp:Label ID="Label1" runat="server">
    </asp:Label>
    <br />
    <asp:ListBox id="ListBox1" 
      Rows="10"
      SelectionMode="Multiple" 
      runat="server">
      <asp:ListItem>Item 1</asp:ListItem>
      <asp:ListItem>Item 2</asp:ListItem>
      <asp:ListItem>Item 3</asp:ListItem>
      <asp:ListItem>Item 4</asp:ListItem>
      <asp:ListItem>Item 5</asp:ListItem>
      <asp:ListItem>Item 6</asp:ListItem>
      <asp:ListItem>Item 7</asp:ListItem>
      <asp:ListItem>Item 8</asp:ListItem>
      <asp:ListItem>Item 9</asp:ListItem>
      <asp:ListItem>Item 10</asp:ListItem>
      <asp:ListItem>Item 11</asp:ListItem>
      <asp:ListItem>Item 12</asp:ListItem>
      <asp:ListItem>Item 13</asp:ListItem>
      <asp:ListItem>Item 14</asp:ListItem>
      <asp:ListItem>Item 15</asp:ListItem>
      <asp:ListItem>Item 16</asp:ListItem>
      <asp:ListItem>Item 17</asp:ListItem>
      <asp:ListItem>Item 18</asp:ListItem>
      <asp:ListItem>Item 19</asp:ListItem>
      <asp:ListItem>Item 20</asp:ListItem>
      <asp:ListItem>Item 21</asp:ListItem>
      <asp:ListItem>Item 22</asp:ListItem>
      <asp:ListItem>Item 23</asp:ListItem>
      <asp:ListItem>Item 24</asp:ListItem>
      <asp:ListItem>Item 25</asp:ListItem>
    </asp:ListBox>

    <asp:button id="Button1"
      Text="Submit" 
      OnClick="SubmitBtn_Click" 
      OnClientClick="setScrollTopInHiddenField();"
      runat="server" />
    <br />
    <asp:Label ID="Label2" runat="server">
    </asp:Label>
  </div>
  </form>
</body>
</html>

@ Page ディレクティブに MaintainScrollPositionOnPostback 属性というものがありますが、たぶんこんな感じでポストバック前後の位置を維持しているのではないかと思います。


------ 2016/1/12 追記 ------

MaintainScrollPositionOnPostBack(ポストバック前後で上下左右のスクロール位置を維持する機能)の仕組みを調べてみました。

やはり、この記事に書いたようなスクロール位置を保存する隠しフィールドとクライアントスクリプトを使用しています。

MaintainScrollPositionOnPostBack でスクロール位置を維持する仕組みについては別の記事「ポストバック前後でスクロール位置維持」に書きましたので興味がありましたら見てください。

Tags: ,

JavaScript

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  June 2021  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar