AngularJS中的ScrollTo函数


73

我正在尝试快速导航以使其正常工作。它漂浮在侧面。当他们单击链接时,会将他们带到页面上的该ID。我正在遵循Treehouse的指南。这就是我要滚动的内容:

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

我最初将其放置在之前</body>。但是我似乎遇到了一种竞争情况,在quickNav编译之前就已经触发了(它ng-hide放在上面,不确定是不是引起了它-但它在DOM内)。

如果我在控制台中运行该代码块,则滚动将按预期进行。

我认为将其移入控制器会更有效-或更可能在指令中。但是我没有实现这一点的运气。如何获得此代码块以与AngularJS一起使用?

Answers:


122

这是一个简单的指令,单击后将滚动到一个元素:

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

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

要获取有关创建指令的帮助,请查看视频,网址http://egghead.io,从#10“第一个指令”开始。

编辑:要使其滚动到href指定的特定元素,只需选中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");
      });
    }
  }
});

然后,您可以像这样使用它:<div scroll-on-click></div>滚动到单击的元素。或<a scroll-on-click href="#element-id"></div>滚动到带有ID的元素。


感谢您对基本指令的帮助。我已经做了一些非常基本的。我不完全确定如何在quicknav中访问href(使用指令)以使其进行锚定链接。
EnigmaRM

1
我最终从您的编辑中删除了几行代码(主要是代码if块。)这将用于滚动到单击的元素(如您在插件中所展示的)正确吗?只是这样,它将更具模块化?
EnigmaRM

任何人都设法使用了此功能并绕过了iOS的“功能”,导致不得不双击以触发“点击”
Simon H

5
@rnrneverdies如果您将$(“ body”)更改为$(“ body,html”),它将在firefox上正常工作
nidal

3
为了获得最佳的跨浏览器支持,您应该使用$(“ html,body”)。animate()
Cory 2015年

32

如果您想使用它,这是一个更好的指令:

您可以滚动到页面中的任何元素:

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

用法(例如,单击div'back-to-top'将滚动到id scroll-top):

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

chrome,firefox,safari和html,body元素的IE原因也支持它。


为什么我需要两个指令而不是一个scroll-to-item=".selector"
不间断的

23

为了动画化到滚动容器内的特定元素(固定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");
}

6

角度解决方案的使用$anchorScroll来自Ben Lesh现已归档的博客文章,在他提供的SO答案中也有详细介绍(包括重写如何在路由中执行此操作):

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

以下是提供此解决方案的博客中的小伙子: http

重要的是要注意,该插件的模板包括此模板,该模板用于设置id您要$anchorScroll滚动到的模板:

<li ng-repeat="item in items" 
    id="item{{item.id}}"
>{{item.name}</li>

如果您只想使用纯JavaScript解决方案,请执行以下操作:

使用父容器ID和目标滚动ID在代码中调用runScroll:

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

参考:跨浏览器JavaScript(不是jQuery ...)滚动到顶部动画


谢谢离子。更新了我的答案
Hasteq 2015年

4

感谢Andy提供的示例,这非常有帮助。由于我正在开发单页滚动并且不希望Angular在使用hashbang URL时刷新,因此我结束了实施略有不同的策略。我还想保留浏览器的后退/前进动作。

我没有使用指令和哈希,而是在$ location.search上使用了$ scope。$ watch,并从那里获取了目标。这提供了一个很好的干净锚标记

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

我将监视代码链接到app.js中的我的模块声明,如下所示:

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

上面代码的警告是,如果您直接从其他路径通过URL访问,则可能无法在jQuery的$ target.offset()调用时及时加载DOM。解决方案是将此代码嵌套在$ viewContentLoaded监视程序中。最终代码如下所示:

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

经过Chrome和FF测试


4

我使用了安德鲁·乔斯林(Andrew Joslin)的答案,该答案效果很好,但触发了有角度的路线更改,从而为我创建了跳动的滚动。如果您要避免触发路线更改,

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


2

另一个建议。一个带有选择器的指令。

HTML:

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

角度:

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

还要注意$ anchorScroll


0

非常清楚的答案,仅使用ANGULARJS,没有任何JQUERY依赖

在您的HTML中底部的某个地方 <back-top>some text</back-top>

在您的html顶部 <div id="top"></div>

在您的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']);
      });
    }
  };
}]);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.