none
ListViewのDIVスクロールでのスクロール位置保持について RRS feed

  • 質問

  • VB.Netを用いてWebページの開発をしています。(VB.Net2013、IE11.0、.NetFramework4.5.2、IIS7.5)

    ListView上に表示されているボタン等のアクションを行っても、

    処理後に表示されるリストビューの位置を保持したいため、以下のような対応を実施しています。

    下記の実装で実現は可能なのですが、一覧を横スクロールして、

    リンククリック等のアクションを行った場合、クリックすると、選択位置が一瞬戻るような動きをしてしまいます。

    (一瞬、左に行って元の位置に戻る)

    何とか、この微妙な動きを回避したいのですが、何か良い方策はないでしょうか。

    ======================================================

    ①aspx(デザイン)にスクロール位置を保存するためのHiddenFieldを追加
    <asp:HiddenField ID="scrollPosTop" runat="server" />
    <asp:HiddenField ID="scrollPosLeft" runat="server" />

    ②aspx(デザイン)にJavaScriptのFunctionを追加
    //スクロール位置の保持
    function setScroll() {
        document.getElementById("scrollPosTop").value = document.getElementById("Div1").scrollTop;
        document.getElementById("scrollPosLeft").value = document.getElementById("Div1").scrollLeft;
    }
    //スクロール位置の復元
    function reScroll() {
        document.getElementById("Div1").scrollTop = document.getElementById("scrollPosTop").value
        document.getElementById("Div1").scrollLeft = document.getElementById("scrollPosLeft").value
    }
    //スクロール位置の初期化
    function firstScroll() {
        document.getElementById("Div1").scrollTop = 0
        document.getElementById("Div1").scrollLeft = 0
    }

    ③aspx(デザイン)にスクロール位置を保持するFunction setScroll()の呼び出しを追加
    スクロール位置を保持したいaspx(デザイン)のコントロールで②で追加したsetScroll()の呼び出しを追加。
    <asp: RadioButton ID="SelectLine" runat="server" ・・・・ onclick="setScroll()" OnCheckedChanged="SelectLine_CheckedChanged" />

    ④サーバイベントハンドラのPage_Loadイベントのポストバックにスクロール位置を復元するFunction reScroll()の呼び出しを追加
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            ・
            ・
        Else
            ・
            ・
            ScriptManager.RegisterStartupScript(Me, Me.GetType, "reScroll", "reScroll();", True)
        End If
    End Sub

    ======================================================

    恐れ入りますが、どうぞよろしくお願いいたします。

    2015年9月8日 5:47

回答

  • コードを見てみました。

    やりたいことは、例えば style="width: 300px; height: 250px; overflow: scroll;" というような style 属性を持つ div 要素の中に、それより幅 / 高さの大きい ListView コントロールを配置して、ユーザーにスクロールしてもらって ListView を閲覧してもらうケースで、ポストバック前後でスクロール位置を保持したいということと理解します。

    そして現在の問題点は、

    > リンククリック等のアクションを行った場合、クリックすると、選択位置が一瞬戻るような動きをしてしまいます。
    > (一瞬、左に行って元の位置に戻る)
    > 何とか、この微妙な動きを回避したいのですが、何か良い方策はないでしょうか。

    ということで、「クリックする」とポストバックがかかり、div 要素の中に ListView が再描画されるがその時点では div 要素のスクロール位置は (0, 0) で、その後少し遅れて RegisterStartupScript で登録したスクリプトが動いてポストバック前のポジションに設定される。その遅れの問題だと思います。

    その理解が正しければ、問題は、

    > ④サーバイベントハンドラのPage_Loadイベントのポストバックにスクロール位置を復元するFunction reScroll()
    > の呼び出しを追加

    というところではないかと思います。

    サーバー側のコードでスクリプトを追加するのでなく、クライアント側の JavaScript で、windows.onload イベントのリスナ(イベントハンドラ)でスクロール位置を設定してやればうまく行くと思います。

    以下のような感じです(ListView の中身は省略していますので注意してください)

    <%@ 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">
    
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Maintain Scroll Position on PostBack</title>
        <script type="text/javascript">
        //<![CDATA[
            function setScrollPositionsInHiddenFields() {
                var top = document.getElementById("div1").scrollTop;
                document.getElementById("<%=ScrollTop.ClientID%>").value = top;
    
                var left = document.getElementById("div1").scrollLeft;
                document.getElementById("<%=ScrollLeft.ClientID%>").value = left;
            }
    
            window.onload = function () {
                var top = document.getElementById("<%=ScrollTop.ClientID%>").value;
                if (top != "") {
                    document.getElementById("div1").scrollTop = top;
                }
    
                var left = document.getElementById("<%=ScrollLeft.ClientID%>").value;
                if (left != "") {
                    document.getElementById("div1").scrollLeft = left;
                }
            }        
        //]]>
        </script>
    
    </head>
    <body>
        <form id="form1" runat="server">
        <h1>Maintain Scroll Position on PostBack</h1>
    
        <asp:HiddenField ID="ScrollTop" runat="server" />
        <asp:HiddenField ID="ScrollLeft" runat="server" />
    
        <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
            ConnectionString="<%$ ConnectionStrings:Northwind %>" 
            SelectCommand="SELECT [ProductID], [ProductName], [SupplierID], [CategoryID], 
                [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued] 
                FROM [Products]">
        </asp:SqlDataSource>
    
        <div id="div1" style="width: 300px; height: 250px; overflow: scroll;">
            <asp:ListView ID="ListView1" runat="server" DataKeyNames="ProductID" 
                DataSourceID="SqlDataSource1">
    
                ・・・省略・・・
    
            </asp:ListView>
        </div>
        <asp:Button ID="Button1" runat="server" Text="PostBack" 
            OnClientClick="setScrollPositionsInHiddenFields();" />
        </form>
    </body>
    </html>


    • 編集済み SurferOnWww 2015年9月9日 5:06 一部修正
    • 回答としてマーク takusan 2015年9月14日 11:04
    2015年9月9日 4:43

すべての返信

  • コードはまだ詳しく読んでないのですが、その前に、@ Page ディレクティブの  MaintainScrollPositionOnPostback 属性を true に設定するという方法ではうまく行かないのかどうか教えてください。

    また、それではうまくいかない場合、期待した動きとどう違うのかも教えてください。


    #適切なフォーラムを選んでスレッドを立てるようお願いします。内容的には VB ではなく、ASP.NET の方が適切です。
    • 編集済み SurferOnWww 2015年9月8日 7:36 #部分追記
    2015年9月8日 7:12
  • コードを見てみました。

    やりたいことは、例えば style="width: 300px; height: 250px; overflow: scroll;" というような style 属性を持つ div 要素の中に、それより幅 / 高さの大きい ListView コントロールを配置して、ユーザーにスクロールしてもらって ListView を閲覧してもらうケースで、ポストバック前後でスクロール位置を保持したいということと理解します。

    そして現在の問題点は、

    > リンククリック等のアクションを行った場合、クリックすると、選択位置が一瞬戻るような動きをしてしまいます。
    > (一瞬、左に行って元の位置に戻る)
    > 何とか、この微妙な動きを回避したいのですが、何か良い方策はないでしょうか。

    ということで、「クリックする」とポストバックがかかり、div 要素の中に ListView が再描画されるがその時点では div 要素のスクロール位置は (0, 0) で、その後少し遅れて RegisterStartupScript で登録したスクリプトが動いてポストバック前のポジションに設定される。その遅れの問題だと思います。

    その理解が正しければ、問題は、

    > ④サーバイベントハンドラのPage_Loadイベントのポストバックにスクロール位置を復元するFunction reScroll()
    > の呼び出しを追加

    というところではないかと思います。

    サーバー側のコードでスクリプトを追加するのでなく、クライアント側の JavaScript で、windows.onload イベントのリスナ(イベントハンドラ)でスクロール位置を設定してやればうまく行くと思います。

    以下のような感じです(ListView の中身は省略していますので注意してください)

    <%@ 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">
    
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Maintain Scroll Position on PostBack</title>
        <script type="text/javascript">
        //<![CDATA[
            function setScrollPositionsInHiddenFields() {
                var top = document.getElementById("div1").scrollTop;
                document.getElementById("<%=ScrollTop.ClientID%>").value = top;
    
                var left = document.getElementById("div1").scrollLeft;
                document.getElementById("<%=ScrollLeft.ClientID%>").value = left;
            }
    
            window.onload = function () {
                var top = document.getElementById("<%=ScrollTop.ClientID%>").value;
                if (top != "") {
                    document.getElementById("div1").scrollTop = top;
                }
    
                var left = document.getElementById("<%=ScrollLeft.ClientID%>").value;
                if (left != "") {
                    document.getElementById("div1").scrollLeft = left;
                }
            }        
        //]]>
        </script>
    
    </head>
    <body>
        <form id="form1" runat="server">
        <h1>Maintain Scroll Position on PostBack</h1>
    
        <asp:HiddenField ID="ScrollTop" runat="server" />
        <asp:HiddenField ID="ScrollLeft" runat="server" />
    
        <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
            ConnectionString="<%$ ConnectionStrings:Northwind %>" 
            SelectCommand="SELECT [ProductID], [ProductName], [SupplierID], [CategoryID], 
                [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued] 
                FROM [Products]">
        </asp:SqlDataSource>
    
        <div id="div1" style="width: 300px; height: 250px; overflow: scroll;">
            <asp:ListView ID="ListView1" runat="server" DataKeyNames="ProductID" 
                DataSourceID="SqlDataSource1">
    
                ・・・省略・・・
    
            </asp:ListView>
        </div>
        <asp:Button ID="Button1" runat="server" Text="PostBack" 
            OnClientClick="setScrollPositionsInHiddenFields();" />
        </form>
    </body>
    </html>


    • 編集済み SurferOnWww 2015年9月9日 5:06 一部修正
    • 回答としてマーク takusan 2015年9月14日 11:04
    2015年9月9日 4:43
  • 返信が大変遅くなり申し訳ありません。

    上記を参考に実装に組み込んだところ、うまく動きました。

    ありがとうございました!

    • 回答としてマーク takusan 2015年9月14日 11:04
    • 回答としてマークされていない takusan 2015年9月14日 11:04
    2015年9月14日 11:04