La modifica del percorso non scorre verso l'alto nella nuova pagina


271

Ho trovato un comportamento indesiderato, almeno per me, quando cambia il percorso. Nel passaggio 11 del tutorial http://angular.github.io/angular-phonecat/step-11/app/#/phones puoi vedere l'elenco dei telefoni. Se scorri verso il basso e fai clic su uno degli ultimi, puoi vedere che lo scorrimento non è in alto, invece è al centro.

L'ho trovato anche in una delle mie app e mi chiedevo come posso farlo scorrere verso l'alto. Posso farlo manualmente, ma penso che ci dovrebbe essere un altro modo elegante per farlo che non conosco.

Quindi, c'è un modo elegante per scorrere verso l'alto quando il percorso cambia?

Risposte:


578

Il problema è che ngView mantiene la posizione di scorrimento quando carica una nuova vista. Puoi indicare $anchorScrolldi "scorrere la finestra dopo aver aggiornato la vista" (i documenti sono un po 'vaghi, ma scorrere qui significa scorrere fino alla cima della nuova vista).

La soluzione è aggiungere autoscroll="true"al tuo elemento ngView:

<div class="ng-view" autoscroll="true"></div>

1
Questo lavoro per me, la pagina scorre bene verso l'alto, ma sto usando scrollTo con la direttiva smoothScroll, ci sono modi per fare uno smoth scroll verso l'alto? inizio pagina?
xzegga,

19
Lo stato dei documenti Se l'attributo è impostato senza valore, abilitare lo scorrimento . Quindi puoi accorciare il codice in modo <div class="ng-view" autoscroll></div>che funzioni esattamente allo stesso modo (a meno che tu non voglia condizionare lo scorrimento).
Daniel Liuzzi,

7
non funziona per me, il dottore non dice che scorrerà verso l'alto
Pencilcheck

6
Trovo che se autoscroll è impostato su true, quando un utente "torna indietro", ritorna all'inizio della pagina, non dove lo ha lasciato, il che è un comportamento indesiderato.
axzr,

4
questo farà scorrere la tua vista fino a questo div. Quindi scorrerà verso l'alto della pagina solo se si trova nella parte superiore della pagina. Altrimenti dovrai eseguire il $window.scrollTo(0,0)listener di eventi $ stateChangeSuccess
Filip Sobczak,

46

Basta mettere questo codice per l'esecuzione

$rootScope.$on("$routeChangeSuccess", function (event, currentRoute, previousRoute) {

    window.scrollTo(0, 0);

});

6
$window.scrollTop(0,0)funziona meglio per me di autoscroll. Nel mio caso ho viste che entrano e escono con transizioni.
IsidroGH,

1
Questo funziona anche meglio per me di autoscroll = "true". Lo scorrimento automatico era troppo tardi per qualche motivo, sarebbe scorrere verso l'alto dopo che la nuova vista era già visibile.
Gregory Bolkenstijn,

5
Questo ha funzionato meglio per me: $ rootScope. $ On ('$ stateChangeSuccess', function () {$ ("html, body"). Animate ({scrollTop: 0}, 200);});
James Gentes,

$ window.scrollTop dovrebbe essere $ window.scrollTo, altrimenti dice che non esiste. Questa soluzione funziona alla grande!
Profeti software

@JamesGentes ne avevo bisogno anche io. Grazie.
Mackenzie Browne,

36

Questo codice ha funzionato benissimo per me .. Spero che funzionerà benissimo anche per te .. Tutto quello che devi fare è solo iniettare $ anchorScroll nel tuo blocco di esecuzione e applicare la funzione listener a rootScope come ho fatto nell'esempio seguente ..

 angular.module('myAngularApp')
.run(function($rootScope, Auth, $state, $anchorScroll){
    $rootScope.$on("$locationChangeSuccess", function(){
        $anchorScroll();
    });

Ecco l'ordine di chiamata del modulo Angularjs:

  1. app.config()
  2. app.run()
  3. directive's compile functions (if they are found in the dom)
  4. app.controller()
  5. directive's link functions (again, if found)

RUN BLOCK viene eseguito dopo la creazione dell'iniettore e viene utilizzato per avviare l'applicazione .. significa che quando si è reindirizzati alla nuova vista del percorso, l'ascoltatore nel blocco di esecuzione chiama il

$ AnchorScroll ()

e ora puoi vedere la pergamena inizia verso l'alto con la nuova vista indirizzata :)


Finalmente una soluzione che funziona con le intestazioni appiccicose! Grazie!
Fabio,

31

Dopo un'ora o due di provare ogni combinazione di ui-view autoscroll=true, $stateChangeStart, $locationChangeStart, $uiViewScrollProvider.useAnchorScroll(), $provide('$uiViewScroll', ...), e molti altri, non ho potuto ottenere scroll-to-top-on-new-page a funzionare come previsto.

Questo è stato alla fine quello che ha funzionato per me. Cattura pushState e replaceState e aggiorna la posizione di scorrimento solo quando si naviga in nuove pagine (il pulsante indietro / avanti mantiene le loro posizioni di scorrimento):

.run(function($anchorScroll, $window) {
  // hack to scroll to top when navigating to new URLS but not back/forward
  var wrap = function(method) {
    var orig = $window.window.history[method];
    $window.window.history[method] = function() {
      var retval = orig.apply(this, Array.prototype.slice.call(arguments));
      $anchorScroll();
      return retval;
    };
  };
  wrap('pushState');
  wrap('replaceState');
})

Funziona benissimo per il mio caso d'uso. Sto usando Angular UI-Router con viste nidificate (diversi livelli di profondità). Grazie!
Joshua Powell,

Cordiali saluti - è necessario essere in modalità HTML5 in angolare per utilizzare pushState: $locationProvider.html5Mode(true); @wkronkel c'è un modo per ottenere questo risultato quando non si utilizza la modalità HTML5 in angolare? Causa il ricaricamento della pagina genera 404 errori a causa della modalità html 5 attiva
britztopher

@britztopher Puoi collegarti agli eventi integrati e mantenere la tua storia, giusto?
Casey,

2
dove lo aggiungi .run? l'angolare il tuo appoggetto?
Philll_t,

1
@Philll_t: Sì, la runfunzione è un metodo disponibile su ogni oggetto del modulo angolare.
Hinrich,

18

Ecco la mia soluzione (apparentemente) robusta, completa e (abbastanza) concisa. Utilizza lo stile compatibile con la minificazione (e l'accesso angular.module (NAME) al modulo).

angular.module('yourModuleName').run(["$rootScope", "$anchorScroll" , function ($rootScope, $anchorScroll) {
    $rootScope.$on("$locationChangeSuccess", function() {
                $anchorScroll();
    });
}]);

PS Ho scoperto che la cosa autoscroll non ha avuto alcun effetto se impostata su vero o falso.


1
Hmmm .. questo scorre la vista alla cima prima e poi la vista cambiamenti sullo schermo per me.
Strelok,

Soluzione pulita che stavo cercando. Se fai clic su "indietro" ti porta dove avevi interrotto - questo potrebbe non essere desiderabile per alcuni, ma nel mio caso mi piace.
mjoyce91,

15

Usando angularjs UI Router, quello che sto facendo è questo:

    .state('myState', {
        url: '/myState',
        templateUrl: 'app/views/myState.html',
        onEnter: scrollContent
    })

Con:

var scrollContent = function() {
    // Your favorite scroll method here
};

Non fallisce mai su nessuna pagina e non è globale.


1
Ottima idea, puoi accorciarla usando:onEnter: scrollContent
zucker

2
Cosa metti dove hai scritto // Your favorite scroll method here?
Agente Zebra,

4
Buon tempismo: ero due righe sopra questo commento nel codice originale, provando ui-router-title . Uso $('html, body').animate({ scrollTop: -10000 }, 100);
Léon Pelletier il

1
Ahah, fantastico! Strano però, non funziona sempre per me. Ho alcune viste che sono così brevi da non avere uno scorrimento, e due viste che hanno uno scorrimento, quando posiziono onEnter: scrollContentsu ciascuna vista funziona solo sulla prima vista / rotta, non sulla seconda. Strano.
Agente Zebra,

1
Non sono sicuro, no, semplicemente non scorre verso l'alto quando dovrebbe. Quando clicco tra le 'rotte', alcune di esse continuano a scorrere verso la posizione di ng-view, anziché nella parte superiore assoluta della pagina allegata. Ha senso?
Agente Zebra,

13

Cordiali saluti per chiunque incontri il problema descritto nel titolo (come ho fatto io) che utilizza anche il plug-in AngularUI Router ...

Come richiesto e risposto in questa domanda SO, il router angolare-ui salta in fondo alla pagina quando si cambiano le rotte.
Non riesci a capire perché la pagina viene caricata in fondo? Problema di scorrimento automatico angolare dell'interfaccia utente

Tuttavia, come afferma la risposta, puoi disattivare questo comportamento dicendo autoscroll="false"sul tuo ui-view.

Per esempio:

<div ui-view="pagecontent" autoscroll="false"></div>
<div ui-view="sidebar" autoscroll="false"></div> 

http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.directive:ui-view


16
Il mio non salta in basso, mantiene solo la posizione di scorrimento invece di andare in alto.
Stephen Smith,

5
Questo non risponde alla domanda. Ho lo stesso problema: quando il percorso cambia il livello di scorrimento rimane nello stesso posto invece di scorrere verso l'alto. Posso farlo scorrere verso l'alto, ma solo cambiando l'hash, cosa che non voglio fare per ovvie ragioni.
Sarà il

7

puoi usare questo javascript

$anchorScroll()

3
Bene, non è così semplice in angularjs. La tua soluzione è valida solo in jquery. Quello che sto cercando è un modo elegante per farlo in angularjs
Matias Gonzalez,

4
Hai provato a usare $ anchorScroll (), è documentato docs.angularjs.org/api/ng.%24anchorScroll .
CodeIsLife

1
FYI Se si specifica una posizione con $location.hash('top')prima $anchorScroll()i pulsanti del browser avanti / indietro predefiniti non funzionano più; continuano a reindirizzarti all'hash nell'URL
Roy

7

Se usi ui-router puoi usare (in esecuzione)

$rootScope.$on("$stateChangeSuccess", function (event, currentState, previousState) {
    $window.scrollTo(0, 0);
});

So che dovrebbe funzionare, sto ampiamente usando "$ stateChangeSuccess", ma per qualche ragione $ window.scrollTo (0,0) non funziona con quello.
Abhas,

4

Ho trovato questa soluzione. Se si passa a una nuova vista, la funzione viene eseguita.

var app = angular.module('hoofdModule', ['ngRoute']);

    app.controller('indexController', function ($scope, $window) {
        $scope.$on('$viewContentLoaded', function () {
            $window.scrollTo(0, 0);
        });
    });

3

Impostare autoScroll su true non ha funzionato per me, quindi ho scelto un'altra soluzione. Ho creato un servizio che si aggancia ogni volta che cambia il percorso e che utilizza il servizio $ anchorScroll incorporato per scorrere verso l'alto. Per me va bene :-).

Servizio:

 (function() {
    "use strict";

    angular
        .module("mymodule")
        .factory("pageSwitch", pageSwitch);

    pageSwitch.$inject = ["$rootScope", "$anchorScroll"];

    function pageSwitch($rootScope, $anchorScroll) {
        var registerListener = _.once(function() {
            $rootScope.$on("$locationChangeSuccess", scrollToTop);
        });

        return {
            registerListener: registerListener
        };

        function scrollToTop() {
            $anchorScroll();
        }
    }
}());

Registrazione:

angular.module("mymodule").run(["pageSwitch", function (pageSwitch) {
    pageSwitch.registerListener();
}]);

3

Un po 'tardi alla festa, ma ho provato tutti i metodi possibili e niente ha funzionato correttamente. Ecco la mia soluzione elegante:

Uso un controller che governa tutte le mie pagine con ui-router. Mi consente di reindirizzare gli utenti che non sono autenticati o convalidati in una posizione appropriata. Molte persone mettono il loro middleware nella configurazione della loro app, ma ho richiesto alcune richieste http, quindi un controller globale funziona meglio per me.

Il mio index.html è simile a:

<main ng-controller="InitCtrl">
    <nav id="top-nav"></nav>
    <div ui-view></div>
</main>

Il mio initCtrl.js è simile a:

angular.module('MyApp').controller('InitCtrl', function($rootScope, $timeout, $anchorScroll) {
    $rootScope.$on('$locationChangeStart', function(event, next, current){
        // middleware
    });
    $rootScope.$on("$locationChangeSuccess", function(){
        $timeout(function() {
            $anchorScroll('top-nav');
       });
    });
});

Ho provato tutte le possibili opzioni, questo metodo funziona al meglio.


1
Questo è l'unico che ha funzionato con il materiale angolare per me, inoltre è id="top-nav"stato importante posizionare l' elemento corretto.
BatteryAcid

2

Tutte le risposte sopra infrangono il comportamento previsto del browser. Ciò che la maggior parte delle persone desidera è qualcosa che scorrerà verso l'alto se si tratta di una "nuova" pagina, ma tornerà alla posizione precedente se ci si arriva tramite il pulsante Indietro (o Avanti).

Se assumi la modalità HTML5, questo risulta essere facile (anche se sono sicuro che alcune persone brillanti là fuori possono capire come renderlo più elegante!):

// Called when browser back/forward used
window.onpopstate = function() { 
    $timeout.cancel(doc_scrolling); 
};

// Called after ui-router changes state (but sadly before onpopstate)
$scope.$on('$stateChangeSuccess', function() {
    doc_scrolling = $timeout( scroll_top, 50 );

// Moves entire browser window to top
scroll_top = function() {
    document.body.scrollTop = document.documentElement.scrollTop = 0;
}

Il modo in cui funziona è che il router presuppone che scorrerà verso l'alto, ma ritarda un po 'per dare al browser la possibilità di finire. Se il browser ci avvisa che la modifica è dovuta a una navigazione avanti / indietro, annulla il timeout e lo scorrimento non si verifica mai.

Ho usato i documentcomandi grezzi per scorrere perché voglio passare all'intera parte superiore della finestra. Se vuoi solo ui-viewscorrere, quindi imposta autoscroll="my_var"dove controlli my_varutilizzando le tecniche sopra. Ma penso che molte persone vorranno scorrere l'intera pagina se vai alla pagina come "nuova".

Quanto sopra usa ui-router, sebbene tu possa usare ng-route invece scambiando $routeChangeSuccessper $stateChangeSuccess.


Come lo implementate? Dove lo inseriresti nel normale codice di percorsi angolari come il seguente? var routerApp = angular.module('routerApp', ['ui.router']); routerApp.config(function($stateProvider, $urlRouterProvider, $locationProvider) { $urlRouterProvider.otherwise('/home'); $locationProvider.html5Mode(false).hashPrefix(""); $stateProvider // HOME STATES AND NESTED VIEWS ======================================== .state('home', { url: '/home', templateUrl: 'partial-home.html' }) });
Agente Zebra,

hmmm ... non ha funzionato :-( Uccide solo l'app. .state('web', { url: '/webdev', templateUrl: 'partial-web.html', controller: function($scope) { $scope.clients = ['client1', 'client2', 'client3']; // I put it here } }) Sto solo imparando queste cose, forse mi manca qualcosa: D
Agente Zebra,

@AgentZebra Come minimo, il controller dovrà prendere $ timeout come input (poiché il mio codice lo fa riferimento), ma forse ancora più importante, il controller che sto menzionando deve vivere a un livello sopra un singolo stato (tu potrebbe includere le dichiarazioni nel controller di livello superiore). La curva di apprendimento iniziale di AngularJS è davvero molto difficile; in bocca al lupo!
Dev93,

2

Nessuna delle risposte fornite ha risolto il mio problema. Sto usando un'animazione tra le viste e lo scorrimento avverrebbe sempre dopo l'animazione. La soluzione che ho trovato in modo che lo scorrimento verso l'alto avvenga prima dell'animazione è la seguente direttiva:

yourModule.directive('scrollToTopBeforeAnimation', ['$animate', function ($animate) {
    return {
        restrict: 'A',
        link: function ($scope, element) {
            $animate.on('enter', element, function (element, phase) {

                if (phase === 'start') {

                    window.scrollTo(0, 0);
                }

            })
        }
    };
}]);

L'ho inserito nella mia vista come segue:

<div scroll-to-top-before-animation>
    <div ng-view class="view-animation"></div>
</div>

2

Soluzione semplice, aggiungere scrollPositionRestorationil modulo di percorso principale abilitato.
Come questo:

const routes: Routes = [

 {
   path: 'registration',
   loadChildren: () => RegistrationModule
},
];

 @NgModule({
  imports: [
   RouterModule.forRoot(routes,{scrollPositionRestoration:'enabled'})
  ],
 exports: [
 RouterModule
 ]
 })
  export class AppRoutingModule { }


0

Ho finalmente ottenuto ciò di cui avevo bisogno.

Avevo bisogno di scorrere verso l'alto, ma volevo che alcune transizioni non lo facessero

Puoi controllarlo a livello di itinerario.
Sto combinando la soluzione sopra di @wkonkel e aggiungendo un semplice noScroll: trueparametro ad alcune dichiarazioni di rotta. Poi lo vedo nella transizione.

Tutto sommato: questo galleggia all'inizio della pagina su nuove transizioni, non galleggia all'inizio Forward/ Backtransizioni e ti consente di ignorare questo comportamento, se necessario.

Il codice: (soluzione precedente più un'opzione extra noScroll)

  // hack to scroll to top when navigating to new URLS but not back/forward
  let wrap = function(method) {
    let orig = $window.window.history[method];
    $window.window.history[method] = function() {
      let retval = orig.apply(this, Array.prototype.slice.call(arguments));
      if($state.current && $state.current.noScroll) {
        return retval;
      }
      $anchorScroll();
      return retval;
    };
  };
  wrap('pushState');
  wrap('replaceState');

Mettilo nel tuo app.runblocco e iniettalo $state...myApp.run(function($state){...})

Quindi, se non desideri scorrere fino all'inizio della pagina, crea un percorso come questo:

.state('someState', {
  parent: 'someParent',
  url: 'someUrl',
  noScroll : true // Notice this parameter here!
})

0

Questo ha funzionato per me incluso lo scorrimento automatico

<div class="ngView" autoscroll="true" >

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.