AngularJS 中的 ScrollTo 函数

IT技术 javascript jquery angularjs scroll anchor
2021-02-02 15:48:02

我正在尝试快速导航以正常工作。它漂浮在一边。当他们点击一个链接时,它会将他们带到页面上的那个 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 一起工作?

6个回答

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

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 的元素。

为了获得最佳的跨浏览器支持,您应该使用 $("html, body").animate()
2021-03-17 15:48:02
任何人都设法使用它并绕过 iOS 的“功能”,导致必须双击才能触发“点击”
2021-03-20 15:48:02
@rnrneverdies 如果将 $("body") 更改为 $("body, html"),它确实适用于 Firefox
2021-03-23 15:48:02
我最终从您的编辑中删除了几行代码(主要是if块)。这将用于滚动到单击的元素(就像您在 plunker 中演示的那样)正确吗?就这样它会更module化?
2021-03-24 15:48:02
感谢您对基本指令的帮助。我已经做了几个非常基本的。我不确定如何访问 quicknav 中的 href(使用指令)以使其执行锚链接。
2021-03-27 15:48:02

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

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

.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 和 IE 也支持 html,body 元素。

为什么我需要两个指令而不是一个scroll-to-item=".selector"
2021-04-13 15:48:02

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

使用$anchorScrollBen 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();
  };
});

这是 plunker,来自提供此解决方案的博客:http ://plnkr.co/edit/xi2r8wP6ZhQpmJrBj1jM?p=preview

重要的是要注意,该 plunker 的模板包含此内容,它设置了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...)滚动到顶部动画

谢谢离子。更新了我的答案
2021-04-05 15:48:02

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

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

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

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

.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 访问,DOM 可能无法及时加载 jQuery 的 $target.offset() 调用。解决方案是将此代码嵌套在 $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 测试