Как сохранить положение прокрутки в MVC?

Im работает над проектом в MVC и с удовольствием узнал об этом. Есть несколько растущих болей, но как только вы их поймете, это не плохо. Одна вещь, которая действительно проста в мире WebForms, - это сохранение положения прокрутки на странице. Все, что нужно сделать, это установить maintainscrollpositiononpostback свойство true. Однако в MVC я не использую обратные связи, поэтому это не будет работать для меня. Каков стандартный способ справиться с этим?

Edit: Ajax приемлемо, но мне также было интересно, как вы это сделаете без AJAX.

11 ответов


способ работы MaintainScrollPositionOnPostback заключается в том, что он имеет пару скрытых полей: __SCROLLPOSITIONX и _ _ SCROLLPOSITIONY

на обратном пути он устанавливает эти,

function WebForm_GetScrollY() {
if (__nonMSDOMBrowser) {
    return window.pageYOffset;
}
else {
    if (document.documentElement && document.documentElement.scrollTop) {
        return document.documentElement.scrollTop;
    }
    else if (document.body) {
        return document.body.scrollTop;
    }
}
return 0;
}
function WebForm_SaveScrollPositionSubmit() {
    if (__nonMSDOMBrowser) {
        theForm.elements['__SCROLLPOSITIONY'].value = window.pageYOffset;
        theForm.elements['__SCROLLPOSITIONX'].value = window.pageXOffset;
    }
    else {
        theForm.__SCROLLPOSITIONX.value = WebForm_GetScrollX();
        theForm.__SCROLLPOSITIONY.value = WebForm_GetScrollY();
    }
    if ((typeof(this.oldSubmit) != "undefined") && (this.oldSubmit != null)) {
        return this.oldSubmit();
    }
    return true;
    }

и затем он вызывает RestoreScrollPosition:

function WebForm_RestoreScrollPosition() {
    if (__nonMSDOMBrowser) {
        window.scrollTo(theForm.elements['__SCROLLPOSITIONX'].value, theForm.elements['__SCROLLPOSITIONY'].value);
    }
    else {
        window.scrollTo(theForm.__SCROLLPOSITIONX.value, theForm.__SCROLLPOSITIONY.value);
    }
    if ((typeof(theForm.oldOnLoad) != "undefined") && (theForm.oldOnLoad != null)) {
        return theForm.oldOnLoad();
    }
    return true;
}

но, как большинство людей сказали, MVC должен избегать обратных связей в любом случае.


Я решил это в JS :

$(document).scroll(function(){
    localStorage['page'] = document.URL;
    localStorage['scrollTop'] = $(document).scrollTop();
});

тогда в документе готово:

$(document).ready(function(){
    if (localStorage['page'] == document.URL) {
        $(document).scrollTop(localStorage['scrollTop']);
    }
});

на самом деле нет стандартного способа обработки этого, это был взлом Microsoft для поддержки их модели post back. Они нуждались в этом, потому что каждый элемент управления делал сообщение назад, и пользователь постоянно возвращался в верхнюю часть страницы.

рекомендация для использования с MVC заключается в том, чтобы сделать большую часть Вашего сообщения обратно на серверы с помощью AJAX. Так что страница не должна повторно передавать фокус не перемещается. jQuery делает AJAX очень простым, и есть даже формы по умолчанию как

<% Ajax.BeginForm(...) %>

который позаботится о стороне AJAX для вас.


принимая вдохновение от WebForms и ответ, предоставленный Ричардом Гадсденом, другой подход с использованием javascript и коллекции форм может выглядеть примерно так:

@{
    var scrollPositionX = string.Empty;        
    if(IsPost) {
        scrollPositionX = Request.Form["ScrollPositionX"];
    }
}

<form action="" method="post">
    <input type="hidden" id="ScrollPositionX" name="ScrollPositionX" value="@scrollPositionX" />
    <input type="submit" id="Submit" name="Submit" value="Go" />
</form>

$("#Submit").click(function () {
    $("#ScrollPositionX").val($(document).scrollTop());
});

$("#ScrollPositionX").each(function () {
    var val = parseInt($(this).val(), 10);
    if (!isNaN(val))
        $(document).scrollTop(val);
});

предоставленный код предназначен для вдохновения и никоим образом не приукрашен. Вероятно, это можно сделать несколькими различными способами, я думаю, все сводится к тому, как вы решите сохранить значение прокрутки вашего документа по почте. Он полностью работает и должен быть cross browser safe, так как мы использование jQuery для прокрутки. Я считаю, что предоставленный код не требует пояснений, но я буду рад предоставить более подробное описание того, что происходит, просто дайте мне знать.


мой собственный обходной путь использует некоторую информацию в ViewData чтобы узнать, какая область должна быть показана в backnavigation, и немного javascript, чтобы расположить курсор страницы:

в представлении такой элемент:

<h3 id="tasks">
    Contained tasks
</h3>

и JavaScript для repositionate странице:

<script type="text/javascript">
    addOnLoad(goAnchor);

    function goAnchor() {
        var paging = <%= //Here you determine (from the ViewData or whatever) if you have to position the element %>;
        if (paging == "True") {
            window.location.hash = "tasks";
        }
</script>

вы могли бы использовать switch чтобы определить, какой элемент со страницы просмотра необходимо переместить.

надеюсь, что это помогает.


<%
   if(!ViewData.ModelState.IsValid)
   {
%>
   window.location.hash = 'Error';
<%
   }
%>

 <a name="Error"></a>

Я использовал атрибуты имен в тегах. Не используется javascript.

страница, на которую я хотел вернуться, имела теги с атрибутом name, например .

страница (вид), которую я вернул из используемого тега Back". Запрос.UrlReferrer используется для перехода на предыдущую страницу. #testname прокручивает позицию страницы до тега с именем "testname".


вот простое, чистое решение Javascript, которое я тестировал только в FF4 и IE9.

идея в том, что это решение должно грациозно деградировать, возвращаясь к стандарту #anchor теги на странице. Что я делаю замены #anchor теги на лету с координатами X и Y, затем при загрузке я просто читаю эти значения из строки запроса и прокручиваю их там. Если по какой-то причине это не удается, браузер все равно должен перейти к #anchor позиция...

разметка:

<a href="/somecontroller/someaction/#someanchor">My Link</a>

jQuery:

$(function() {

// RESTORE SCROLL POSITION
RestoreScrollPosition();

// SAVE SCROLL POSITION
$('a:not(a[href^="http"])').filter('[href$="#someanchor"]').each(function() {
    $(this).click(function() {
        var href = $(this).attr('href').replace("#someanchor","");
        if (href.indexOf('?') == -1) {
            href = href + '?x='
        } else {
            href = href + '&x='
        }
        href = href + window.pageXOffset;
        href = href + '&y=' + window.pageYOffset;
        $(this).attr('href', href);
    });
});
}

несколько вспомогательных методов:

function RestoreScrollPosition() {

    var scrollX = gup('x');
    var scrollY = gup('y');

    if (scrollX != null && scrollY != null) {
        window.scrollTo(scrollX, scrollY);
        return true;
    }
    return false;
}

function gup(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regexS = "[\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(window.location.href);
    if (results == null)
        return "";
    else
        return results[1];
}

это соответствует моим потребностям, но может быть более общим/многоразовым - я был бы рад, если бы кто-то улучшил это... :-)


очень нехороший способ сделать это-использовать cookies.

Если вы используете одну страницу в своем MVC, которая обрабатывает другие страницы, вы можете использовать фрагмент кода, который загружает каждую страницу, которая создает cookie (если его нет), называемый "scrolltop". Есть способы автоматического обновления этого файла cookie javascript, когда пользователь прокручивает вверх или вниз, улавливая эти события или просматривая значение scrollTop.

на новой странице вам просто нужно загрузить сохраненную позицию и сделать просмотрите прокрутку там за 0 миллисекунд (с помощью Mootools или любого сценария Ajax это должно быть возможно), и пользователь будет точно там, где они были.

Я мало знаю о asp, поэтому я не знаю, существует ли метод привязки к текущей y-позиции. JavaScript-это быстрый и простой способ. Якоря в HTMl могут быть опцией, если вы привязали каждый элемент и разместили якорь на других страницах.


я использую .scrollTop, как показано ниже, очень легко, он даже работает с несколькими формами в представлении (у меня очень длинный вид, разбитый на несколько форм):

сначала поместите это свойство внутри модели:

               public string scrollTop { get; set; }

и в представлении, внутри формы #1:

               @Html.HiddenFor(m => m.scrollTop, new {@id="ScrollForm1"})

внутренняя форма #2:

               @Html.HiddenFor(m => m.scrollTop, new {@id="ScrollForm2"})

внутренняя форма #2:

               @Html.HiddenFor(m => m.scrollTop, new {@id="ScrollForm3"})

и затем в нижней части представления:

 $(document).ready(function () {
    $(document).scrollTop(@Model.scrollTop);
    $(document).scroll(function () {
        $("#ScrollForm1").val($(document).scrollTop());
        $("#ScrollForm2").val($(document).scrollTop());
        $("#ScrollForm3").val($(document).scrollTop());
      });
   });

ваша позиция прокрутки всегда сохраняется после обратной передачи, потому что @Html.Поля HiddenFor хранят текущий свиток и передают его модели на post. А затем, когда страница появляется, она получает значение scrollTop от модели. В конце ваша страница будет вести себя как webform, все останется нетронутым.


@{

}

<html>

<head>
    <script type="text/javascript">

window.onload = function () {
    var div = document.getElementById("dvScroll");
   var div_position = document.getElementById("div_position");
    var position = parseInt(@Request.Form("div_position"));
    if (isNaN(position)) {
        position = 0;
    }

    div.scrollTop = position;
    div.onscroll = function () {
        div_position.value = div.scrollTop;
    };
};

</script>
</head>

<body>

<div id="dvScroll" style="overflow-y: scroll; height: 260px; width: 300px">

    1. This is a sample text

    <br />

    2. This is a sample text

    <br />

    3. This is a sample text

    <br />

    4. This is a sample text

    <br />

    5. This is a sample text

    <br />

    6. This is a sample text

    <br />

    7. This is a sample text

    <br />

    8. This is a sample text

    <br />

    9. This is a sample text

    <br />

    10. This is a sample text

    <br />

    11. This is a sample text

    <br />

    12. This is a sample text

    <br />

    13. This is a sample text

    <br />

    14. This is a sample text

    <br />

    15. This is a sample text

    <br />

    16. This is a sample text

    <br />

    17. This is a sample text

    <br />

    18. This is a sample text

    <br />

    19. This is a sample text

    <br />

    20. This is a sample text

    <br />

    21. This is a sample text

    <br />

    22. This is a sample text

    <br />

    23. This is a sample text

    <br />

    24. This is a sample text

    <br />

    25. This is a sample text

    <br />

</div>

<hr />
<form method="post">
<input type="hidden" id="div_position" name="div_position" />
<input type="submit" value="Cool" />
    </form> 
</body>
</html>

вы можете использовать это для поддержания положения прокрутки после обратной передачи.

источник: http://www.aspsnippets.com/Articles/Maintain-Scroll-Position-of-DIV-on-PostBack-in-ASPNet.aspx