ScrollTo-Funktion in AngularJS

72

Ich versuche, ein schnelles Navigationsgerät richtig zum Laufen zu bringen. Es schwebt auf der Seite. Wenn sie auf einen Link klicken, gelangen sie zu dieser ID auf der Seite. Ich folge diesem Leitfaden von Treehouse . Folgendes habe ich für das Scrollen:

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

Ich habe es zunächst vor das gestellt </body>. Aber ich scheine in eine Rennbedingung zu geraten, in der das ausgelöst wurde, bevor das quickNav kompiliert wurde (es ist darauf ng-hideplatziert, nicht sicher, ob es das verursacht - aber es befindet sich im DOM).

Wenn ich diesen Codeblock in der Konsole ausführe, funktioniert das Scrollen wie erwartet.

Ich dachte, es wäre effektiver, dies in den Controller zu verschieben - oder eher innerhalb einer Direktive. Aber ich habe kein Glück, das zu erreichen. Wie kann ich diesen Codeblock für AngularJS verwenden?

EnigmaRM
quelle

Antworten:

122

Hier ist eine einfache Anweisung, die beim Klicken zu einem Element blättert:

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

Demo: http://plnkr.co/edit/yz1EHB8ad3C59N6PzdCD?p=preview

Weitere Informationen zum Erstellen von Anweisungen finden Sie in den Videos unter http://egghead.io , beginnend mit Nummer 10 "Erste Anweisung".

Bearbeiten : Um zu einem bestimmten Element zu scrollen, das durch eine href angegeben ist, überprüfen Sie einfach 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");
      });
    }
  }
});

Dann könnten Sie es so verwenden: <div scroll-on-click></div>um zu dem angeklickten Element zu scrollen. Oder <a scroll-on-click href="#element-id"></div>um mit der ID zum Element zu scrollen.

Andrew Joslin
quelle
Vielen Dank für die Hilfe bei einer grundlegenden Anweisung. Ich habe schon ein paar sehr einfache gemacht. Ich bin mir nicht ganz sicher, wie ich auf das href im QuickNav zugreifen würde (mithilfe einer Direktive), damit es die Ankerverknüpfung ausführt.
EnigmaRM
1
ifAm Ende habe ich mehrere Codezeilen aus Ihrer Bearbeitung entfernt (meistens nur den Block). Das würde verwendet, um zu einem Element zu scrollen, auf das geklickt wurde (wie Sie in Ihrem Plunker gezeigt haben). Richtig? Nur damit es modularer wäre?
EnigmaRM
Jeder hat es geschafft, dies zu nutzen und die iOS-Funktion zu umgehen, die dazu führt, dass zweimal getippt werden muss, um einen Klick auszulösen
Simon H
5
@rnrneverdies es funktioniert auf Firefox, wenn Sie $ ("body") in $ ("body, html")
ändern
3
Für die beste browserübergreifende Unterstützung sollten Sie $ ("html, body") verwenden. Animate ()
Cory
32

Dies ist eine bessere Richtlinie, falls Sie sie verwenden möchten:

Sie können zu jedem Element auf der Seite scrollen:

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

Verwendung (zum Beispiel klicken Sie auf div 'back-to-top', um zu id scroll-top zu scrollen):

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

Es wird auch von Chrome, Firefox, Safari und IE aufgrund des HTML-Body-Elements unterstützt.

Liad Livnat
quelle
Warum brauche ich zwei Anweisungen statt einer scroll-to-item=".selector"?
Undefitied
23

Um ein bestimmtes Element in einem Bildlaufcontainer zu animieren (fester DIV)

/*
    @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");
}
teter
quelle
6

Eine eckige Lösung mit $ 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();
  };
});

Und hier ist ein Plunk: http://plnkr.co/edit/xi2r8wP6ZhQpmJrBj1jM?p=preview

Und wenn Sie sich für eine reine Javascript-Lösung interessieren, finden Sie hier eine:

Rufen Sie runScroll in Ihrem Code mit der übergeordneten Container-ID und der Ziel-Scroll-ID auf:

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

Referenz: Browserübergreifendes JavaScript (nicht jQuery ...) zur oberen Animation scrollen

Hasteq
quelle
Danke Ionic. Aktualisiert meine Antwort
Hasteq
4

Danke Andy für das Beispiel, das war sehr hilfreich. Ich habe die Implementierung einer etwas anderen Strategie beendet, da ich einen einseitigen Bildlauf entwickle und nicht wollte, dass Angular bei Verwendung der Hashbang-URL aktualisiert wird. Ich möchte auch die Vorwärts- / Rückwärtsbewegung des Browsers beibehalten.

Anstatt die Direktive und den Hash zu verwenden, verwende ich einen $ scope. $ Watch auf der $ location.search und erhalte das Ziel von dort. Dies ergibt einen schönen sauberen Ankertag

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

Ich habe den Watchcode wie folgt an meine Moduldeklaration in app.js gekettet:

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

Die Einschränkung mit dem obigen Code besteht darin, dass das DOM möglicherweise nicht rechtzeitig für den Aufruf von jQuery $ target.offset () geladen wird, wenn Sie über eine URL direkt von einer anderen Route aus zugreifen. Die Lösung besteht darin, diesen Code in einem $ viewContentLoaded-Watcher zu verschachteln. Der endgültige Code sieht ungefähr so ​​aus:

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

Getestet mit Chrome und FF

Marc Gibbons
quelle
4

Ich habe Andrew Joslins Antwort verwendet, die großartig funktioniert, aber eine Änderung der Winkelroute ausgelöst hat, die für mich eine nervös aussehende Schriftrolle erzeugt hat. Wenn Sie vermeiden möchten, dass eine Routenänderung ausgelöst wird,

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;
      });
    }
  }
});
Danyamachine
quelle
2

Was ist mit Angular-Scroll , es wird aktiv gepflegt und es gibt keine Abhängigkeit von jQuery.

Marwen Trabelsi
quelle
2

Ein weiterer Vorschlag. Eine Direktive mit Selektor.

HTML:

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

Winkel:

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

Beachten Sie auch $ anchorScroll

Ungeeignet
quelle
0

Sehr klare Antwort, nur mit ANGULARJS, keine JQUERY hängt davon ab

in Ihrem HTML irgendwo unten <back-top>some text</back-top>

in Ihrem HTML irgendwo oben <div id="top"></div>

in deinem 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']);
      });
    }
  };
}]);
Anja Ishmukhametova
quelle