web-dev-qa-db-fra.com

Fonction ScrollTo dans AngularJS

J'essaie d'obtenir un nav rapide pour fonctionner correctement. Il flotte sur le côté. Lorsqu'ils cliquent sur un lien, ils se dirigent vers cet identifiant sur la page. Je suis ce guide de Treehouse . Voici ce que j'ai pour le défilement:

$("#quickNav a").click(function(){
    var quickNavId = $(this).attr("href");
    $("html, body").animate({scrollTop: $(location).offset().top}, "slow");
    return false;
});

Je l'ai d'abord placé avant le </body>. Mais il semble que je me retrouve dans une situation de concurrence critique où le tir a eu lieu avant la compilation de quickNav (il y a un ng-hide placé dessus, je ne suis pas sûr que cela soit la cause - mais cela se trouve dans le DOM).

Si je lance ce bloc de code dans la console, le défilement fonctionne comme prévu.

J'ai pensé qu'il serait plus efficace de transférer cela dans le contrôleur - ou plus probablement dans une directive. Mais je n'ai pas de chance pour accomplir cela. Comment puis-je faire en sorte que ce bloc de code fonctionne avec AngularJS?

69
EnigmaRM

Voici une directive simple qui permet de faire défiler un élément au clic:

myApp.directive('scrollOnClick', function() {
  return {
    restrict: 'A',
    link: function(scope, $Elm) {
      $Elm.on('click', function() {
        $("body").animate({scrollTop: $Elm.offset().top}, "slow");
      });
    }
  }
});

Démo: http://plnkr.co/edit/yz1EHB8ad3C59N6PzdCD?p=preview

Pour obtenir de l’aide sur la création de directives, regardez les vidéos sur http://egghead.io , à partir de la 10e "première directive".

edit : pour le faire défiler jusqu'à un élément spécifique spécifié par un href, cochez simplement attrs.href.

myApp.directive('scrollOnClick', function() {
  return {
    restrict: 'A',
    link: function(scope, $Elm, attrs) {
      var idToScroll = attrs.href;
      $Elm.on('click', function() {
        var $target;
        if (idToScroll) {
          $target = $(idToScroll);
        } else {
          $target = $Elm;
        }
        $("body").animate({scrollTop: $target.offset().top}, "slow");
      });
    }
  }
});

Vous pouvez ensuite l'utiliser comme ceci: <div scroll-on-click></div> pour faire défiler jusqu'à l'élément sur lequel vous avez cliqué. Ou <a scroll-on-click href="#element-id"></div> pour faire défiler l'élément avec l'identifiant.

122
Andrew Joslin

C'est une meilleure directive dans le cas où vous voudriez l'utiliser:

vous pouvez faire défiler n'importe quel élément de la page:

.directive('scrollToItem', function() {                                                      
    return {                                                                                 
        restrict: 'A',                                                                       
        scope: {                                                                             
            scrollTo: "@"                                                                    
        },                                                                                   
        link: function(scope, $Elm,attr) {                                                   

            $Elm.on('click', function() {                                                    
                $('html,body').animate({scrollTop: $(scope.scrollTo).offset().top }, "slow");
            });                                                                              
        }                                                                                    
    }})     

Utilisation (par exemple, cliquez sur div "back-to-top" pour accéder à id scroll-top):

<a id="top-scroll" name="top"></a>
<div class="back-to-top" scroll-to-item scroll-to="#top-scroll"> 

Il est également supporté par chrome, firefox, safari et IE cause de l'élément html, body.

31
Liad Livnat

Pour animer un élément spécifique dans un conteneur de défilement (DIV fixe)

/*
    @param Container(DIV) that needs to be scrolled, ID or Div of the anchor element that should be scrolled to
    Scrolls to a specific element in the div container
*/
this.scrollTo = function(container, anchor) {
    var element = angular.element(anchor);
    angular.element(container).animate({scrollTop: element.offset().top}, "slow");
}
22
teter

Une solution angular utilisant $ anchorScroll http://www.benlesh.com/2013/02/angular-js-scrolling-to-element-by-id.html :

app.controller('MainCtrl', function($scope, $location, $anchorScroll) {
  var i = 1;

  $scope.items = [{ id: 1, name: 'Item 1' }];

  $scope.addItem = function (){
    i++;
    //add the item.
    $scope.items.Push({ id: i, name: 'Item ' + i});
    //now scroll to it.
    $location.hash('item' + i);
    $anchorScroll();
  };
});

Et voici un plunk: http://plnkr.co/edit/xi2r8wP6ZhQpmJrBj1jM?p=preview

Et si vous vous souciez d'une solution javascript pure, en voici une:

Appelez runScroll dans votre code avec l'ID de conteneur parent et l'ID de défilement cible:

function runScroll(parentDivId,targetID) {
    var longdiv;
    longdiv = document.querySelector("#" + parentDivId);
    var div3pos = document.getElementById(targetID).offsetTop;
    scrollTo(longdiv, div3pos, 600);
}


function scrollTo(element, to, duration) {
    if (duration < 0) return;
    var difference = to - element.scrollTop;
    var perTick = difference / duration * 10;

    setTimeout(function () {
        element.scrollTop = element.scrollTop + perTick;
        if (element.scrollTop == to) return;
        scrollTo(element, to, duration - 10);
    }, 10);
}

Référence: navigateur JavaScript croisé (pas jQuery ...) défiler vers le haut de l'animation

6
Hasteq

J'ai utilisé la réponse d'Andrew joslin, qui fonctionne très bien mais qui a provoqué un changement de route angular, qui a créé un parchemin d'apparence nerveuse. Si vous souhaitez éviter de provoquer un changement d'itinéraire,

myApp.directive('scrollOnClick', function() {
  return {
    restrict: 'A',
    link: function(scope, $Elm, attrs) {
      var idToScroll = attrs.href;
      $Elm.on('click', function(event) {
        event.preventDefault();
        var $target;
        if (idToScroll) {
          $target = $(idToScroll);
        } else {
          $target = $Elm;
        }
        $("body").animate({scrollTop: $target.offset().top}, "slow");
        return false;
      });
    }
  }
});
4
danyamachine

Merci Andy pour l'exemple, c'était très utile. J'ai fini de mettre en œuvre une stratégie légèrement différente, car je développais un parchemin d'une page et que je ne voulais pas que Angular s'actualise lors de l'utilisation de l'URL hashbang. Je souhaite également préserver l'action précédent/suivant du navigateur.

Au lieu d'utiliser la directive et le hash, j'utilise un $ scope. $ Watch sur le $ location.search et j'obtiens la cible à partir de là. Cela donne une balise Nice clean anchor

<a ng-href="#/?scroll=myElement">My element</a>

J'ai chaîné le code de la montre à la déclaration de mon module dans app.js comme suit:

.run(function($location, $rootScope) {
   $rootScope.$watch(function() { return $location.search() }, function(search) { 
     var scrollPos = 0;
     if (search.hasOwnProperty('scroll')) {
       var $target = $('#' + search.scroll);
       scrollPos = $target.offset().top;
     }   
     $("body,html").animate({scrollTop: scrollPos}, "slow");
   });
})

L'avertissement avec le code ci-dessus est que si vous accédez directement par URL à partir d'une autre route, il se peut que le DOM ne soit pas chargé à temps pour l'appel de $ target.offset () de jQuery. La solution consiste à imbriquer ce code dans un observateur $ viewContentLoaded. Le code final ressemble à ceci:

.run(function($location, $rootScope) {
  $rootScope.$on('$viewContentLoaded', function() {
     $rootScope.$watch(function() { return $location.search() }, function(search) {
       var scrollPos = 0 
       if (search.hasOwnProperty('scroll')) {
         var $target = $('#' + search.scroll);
         var scrollPos = $target.offset().top;
       }
       $("body,html").animate({scrollTop: scrollPos}, "slow");                                                                                                                                                                    
     });  
   });    
 })

Testé avec Chrome et FF

4
Marc Gibbons

Qu'en est-il de angular-scroll , il est maintenu activement et il n'y a aucune dépendance à jQuery ..

2
Marwen Trabelsi

Une autre suggestion. Une directive avec sélecteur.

HTML:

<button type="button" scroll-to="#catalogSection">Scroll To</button>

Angulaire:

app.directive('scrollTo', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.on('click', function () {

                var target = $(attrs.scrollTo);
                if (target.length > 0) {
                    $('html, body').animate({
                        scrollTop: target.offset().top
                    });
                }
            });
        }
    }
});

Avis également $ anchorScroll

1
Undefitied

réponse très claire, en utilisant seulement ANGULARJS, pas de JQUERY dépend

dans votre html quelque part sur le bas <back-top>some text</back-top>

dans votre html quelque part sur le dessus <div id="top"></div>

dans votre js:

/**
 * @ngdoc directive
 * @name APP.directive:backTop
 <pre>
<back-top></back-top>
 </pre>
 */


angular
.module('APP')
.directive('backTop', ['$location', '$anchorScroll' ,function($location, $anchorScroll) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    template: '<span class=\'btn btn-mute pull-right\'><i class=\'glyphicon glyphicon-chevron-up\'></i><ng-transclude></ng-transclude></span>',
    scope: {
    },
    link: function(scope, element) {
      element.on('click', function(event) {
        $anchorScroll(['top']);
      });
    }
  };
}]);
0
Anja Ishmukhametova