web-dev-qa-db-fra.com

Comment maintenir la position de défilement dans MVC?

Je travaille sur un projet dans MVC et j'ai aimé apprendre à ce sujet. Il y a quelques problèmes de croissance, mais une fois que vous les avez compris, ce n'est pas grave. Une chose très simple dans le monde WebForms est de conserver la position de défilement sur une page. Tout ce que vous faites est de définir la propriété MaintainScrollPositionOnPostback sur true. Cependant, dans MVC, je n'utilise pas de publications afin que cela ne fonctionne pas pour moi. Quel est le moyen standard de gérer cela?

Edit: Ajax est acceptable, mais je me demandais également comment vous pourriez le faire sans AJAX.

24
Papa Burgundy

MaintainScrollPositionOnPostback fonctionne comme suit: il contient une paire de champs cachés: __SCROLLPOSITIONX et __SCROLLPOSITIONY

Sur un postback, il définit ceux-ci,

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;
    }

puis il appelle 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;
}

Mais comme la plupart des gens l'ont dit, MVC devrait de toute façon éviter les publications.

8
Richard Gadsden

J'ai résolu ceci dans JS:

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

Puis dans le document prêt:

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

En fait, il n’existe aucun moyen standard de gérer cela, c’était un bidouillage de Microsoft pour supporter leur modèle de post back. Ils en avaient besoin parce que chaque contrôle effectuait une publication en retour et que l'utilisateur était constamment repoussé en haut de la page.

La recommandation d'utilisation avec MVC consiste à effectuer la majeure partie de votre publication sur des serveurs utilisant AJAX. Pour que la page ne soit pas obligée de rendre le focus, le focus n'est pas déplacé. jQuery facilite AJAX, et il existe même des formulaires par défaut tels que

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

Ce qui s'occupera du côté AJAX des choses pour vous.

6
Nick Berardi

En s'inspirant de WebForms et de la réponse fournie par Richard Gadsden, une autre approche utilisant javascript et la collection de formulaires pourrait ressembler à ceci:

@{
    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);
});

Le code fourni est une source d’inspiration et n’est en aucun cas prétentieux. Cela pourrait probablement être fait de différentes manières. Je suppose que tout dépend de la façon dont vous décidez de conserver la valeur scrollTop de votre document dans le POST. Il fonctionne parfaitement et doit être sécurisé pour tous les navigateurs, car nous utilisons jQuery pour le défilement. Je crois que le code fourni est explicite, mais je me ferai un plaisir de fournir une description plus détaillée de ce qui se passe, faites le moi savoir.

4
Sniffdk

Ma propre solution de contournement utilise certaines informations dans la variable ViewData pour savoir quelle zone doit être affichée dans la navigation en arrière-plan et un petit javascript pour positionner le curseur de la page:

Dans la vue, un élément comme celui-ci:

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

Et le javascript pour repositionner la page:

<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>

Vous pouvez utiliser une switch pour déterminer quel élément de la page de vue vous devez déplacer.

J'espère que ça aide.

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

 <a name="Error"></a>
2
Mike Flynn

Voici une solution Javascript simple et pure que j'ai testée dans FF4 et IE9 uniquement.

L'idée est que cette solution devrait se dégrader gracieusement en retombant sur les balises #anchor standard d'une page. Ce que je fais est de remplacer ces balises #anchor à la volée par les coordonnées X et Y, puis lors du chargement, je lis simplement ces valeurs à partir de la chaîne de requête et je les fais défiler. Si cela échoue pour une raison quelconque, le navigateur doit toujours accéder à la position #anchor ...

Balisage:

<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);
    });
});
}

Quelques méthodes d'assistance:

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];
}

Cela correspond à mes besoins, mais pourrait être plus générique/réutilisable - je serais heureux que quelqu'un améliore ce point ... :-)

1
user744322

J'ai utilisé des attributs de nom dans les balises. Pas de javascript utilisé.

La page sur laquelle je voulais revenir comportait <a> balises avec attribut name, par exemple. <a name="testname">.

La page (vue) que j'ai renvoyée de la balise utilisée <a href="<%: Request.UrlReferrer %> #testname "> Retour </a>". Request.UrlReferrer est utilisé pour aller à la page précédente. #Testname fait défiler la page. position à taguer avec le nom "testname".

1
the1johan

une façon très peu agréable de faire cela consiste à utiliser des cookies.

Si vous utilisez UNE page dans votre MVC qui gère les autres pages, vous pouvez insérer un extrait de code dans celle-ci, qui charge chaque page créant un cookie (s’il n’existe pas) appelé "scrolltop". Il existe des moyens pour que javascript mette automatiquement ce cookie à jour lorsque l'utilisateur fait défiler l'affichage en saisissant ces événements ou en observant la valeur scrollTop.

Sur une nouvelle page, il vous suffit de charger la position enregistrée et de faire défiler la vue en 0 millisecondes (avec Mootools ou tout script Ajax, cela devrait être possible) et l'utilisateur sera exactement où il se trouvait.

Je ne connais pas beaucoup d'asp, je ne sais donc pas s'il existe une méthode d'ancrage à la position y actuelle. Javascript est un moyen rapide et facile. Les ancres dans HTMl peuvent être une option si vous avez ancré chaque élément et posté l’ancre sur d’autres pages.

0
xaddict

J'utilise .scrollTop comme indiqué ci-dessous, très facilement, cela fonctionne même avec plusieurs formes dans la vue (j'ai une très longue vue, décomposée en plusieurs formes):

Commencez par placer cette propriété dans le modèle:

               public string scrollTop { get; set; }

Et dans la vue, à l'intérieur du formulaire n ° 1:

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

dans le formulaire n ° 2:

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

dans le formulaire n ° 2:

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

puis au bas de la vue:

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

Votre position de défilement est toujours conservée lors de la publication car les champs @ Html.HiddenFor stockent votre défilement actuel et le transmettent au modèle lors de la publication. Et puis, quand la page apparaît, elle obtient la valeur scrollTop du modèle. À la fin, votre page se comporterait comme un formulaire Web, tout reste intact.

0
Yogi