保留 AngularJS 中路由更改的滚动位置?

IT技术 javascript angularjs
2021-02-22 13:40:34

示例应用程序:http : //angular.github.com/angular-phonecat/step-11/app/#/phones

如果您选择最后一款手机“摩托罗拉魅力”,它会显示手机的详细信息。当您在浏览器上导航回时,它会重新加载数据并且滚动位于顶部。

导航返回时自动滚动到剩余位置的最佳方法是什么?而且,为什么 angular 会重新加载数据?

我的计算机上有相同的“angular-phonecat”示例,并且我添加了一个无限滚动,它会在您滚动时加载更多数据。所以我真的不希望用户再次重新加载 50 多个项目或向下滚动 30 秒。

6个回答

我在这里有一个小提琴,它显示了如何在详细视图之后恢复列表视图中的滚动位置;尚未封装在指令中,正在处理...

http://jsfiddle.net/BkXyQ/6/

$scope.scrollPos = {}; // scroll position of each view

$(window).on('scroll', function() {
    if ($scope.okSaveScroll) { // false between $routeChangeStart and $routeChangeSuccess
        $scope.scrollPos[$location.path()] = $(window).scrollTop();
        //console.log($scope.scrollPos);
    }
});

$scope.scrollClear = function(path) {
    $scope.scrollPos[path] = 0;
}

$scope.$on('$routeChangeStart', function() {
    $scope.okSaveScroll = false;
});

$scope.$on('$routeChangeSuccess', function() {
    $timeout(function() { // wait for DOM, then restore scroll position
        $(window).scrollTop($scope.scrollPos[$location.path()] ? $scope.scrollPos[$location.path()] : 0);
        $scope.okSaveScroll = true;
    }, 0);
});

小提琴还显示了在“ListCtrl”之外获取列表一次。

@JPOD 请参阅我对此代码指令的回答。stackoverflow.com/questions/14107531/...
2021-05-02 13:40:34
正是我要找的那个。谢谢你。
2021-05-05 13:40:34
我有类似的东西,但是当页面滚动到顶部时发现有闪烁,然后我们将其滚动回底部。你有没有找到避免这种情况的方法?
2021-05-06 13:40:34
您是否最终将其封装在指令中?
2021-05-10 13:40:34
Angular 应用程序应该使用 $window 而不是 $(window) 因为 Angular 应用程序不应该使用 jQuery(内置 int lite 版本除外)。
2021-05-14 13:40:34

下面是另一种版本的 keep-scroll-pos 指令。这个版本

  • 记住$routeProvider 定义的每个templateUrl滚动位置

  • 尊重哈希标签,例如 #/home# section-2,将滚动到#section-2而不是之前的滚动位置。

  • 易于使用,因为它是独立的,并在内部存储滚动位置。

html 使用示例:

<div ng-view keep-scroll-pos></div>

keepScrollPos 指令的代码如下:

“严格使用”;

angular.module("myApp.directives", [])

.directive("keepScrollPos", function($route, $window, $timeout, $location, $anchorScroll) {

    // 缓存每个路由的 templateUrl 的滚动位置
    var scrollPosCache = {};

    // 编译函数
    返回函数(范围,元素,属性){

        scope.$on('$routeChangeStart', function() {
            // 存储当前视图的滚动位置
            如果($route.current){
                scrollPosCache[$route.current.loadedTemplateUrl] = [ $window.pageXOffset, $window.pageYOffset ];
            }
        });

        scope.$on('$routeChangeSuccess', function() {
            // 如果显式指定了哈希,则它胜过先前存储的滚动位置
            如果 ($location.hash()) {
                $anchorScroll();

            // 否则获取上一个滚动位置;如果没有,滚动到页面顶部
            } 别的 {
                var prevScrollPos = scrollPosCache[$route.current.loadedTemplateUrl] || [ 0, 0 ];
                $超时(功能(){
                    $window.scrollTo(prevScrollPos[0], prevScrollPos[1]);
                }, 0);
            }
        });
    }
});

要忽略先前存储的滚动位置,并强制滚动到顶部,请使用伪哈希标签:#top,例如 href=" #/home#top "。

或者,如果您更喜欢始终滚动到顶部,请使用内置的 ng-view自动滚动选项:

<div ng-view autoscroll></div>
这个解决方案最适合我。我使用 ui 路由器进行路由,因此我将所有出现的 $route 替换为 $state。否则,不需要其他更改。我也喜欢 $window 和 $anchorScroll 的使用方式,而不是在其他一些建议的答案中使用的依赖于 jquery 的 $(window)。
2021-04-29 13:40:34

我使用了@Joseph Oster 的解决方案来创建指令。我还冒昧地更新了答案以使用:

  • $locationChangeStart
  • $locationChangeSuccess

因为其他事件已经过时了。

小提琴在这里:http : //jsfiddle.net/empie/p5pn3rvL/

指令来源:

angular.module('myapp', ['ngRoute'])
    .directive('autoScroll', function ($document, $timeout, $location) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            scope.okSaveScroll = true;

            scope.scrollPos = {};

            $document.bind('scroll', function () {
                if (scope.okSaveScroll) {
                    scope.scrollPos[$location.path()] = $(window).scrollTop();
                }
            });

            scope.scrollClear = function (path) {
                scope.scrollPos[path] = 0;
            };

            scope.$on('$locationChangeSuccess', function (route) {
                $timeout(function () {
                    $(window).scrollTop(scope.scrollPos[$location.path()] ? scope.scrollPos[$location.path()] : 0);
                    scope.okSaveScroll = true;
                }, 0);
            });

            scope.$on('$locationChangeStart', function (event) {
                scope.okSaveScroll = false;
            });
        }
    };
})
很好的指令,但我想知道它是否可以在ui路由器中使用?
2021-04-30 13:40:34
如果控制器/路由发生更改,此解决方案将不起作用
2021-05-04 13:40:34
它在小提琴上工作正常......你希望看到什么,你在尝试什么?
2021-05-11 13:40:34
@hariszaman 也许您遇到了这个非常好的但简洁的解决方案的局限性之一。试试这个尽管如果由于您使用 ui-router 而无法正常工作,您可能必须将调用换成$routeChangeX对等项
2021-05-13 13:40:34
如果小提琴没有显示朋友列表或表格,这可能是因为当小提琴是 HTTPS 时,您的浏览器阻止了通过 HTTP 调用 angular-route JS 文件。只需删除 angular-route.js '外部资源' 并使用 HTTPS 前缀将其添加回来。
2021-05-20 13:40:34

我以前没有使用过它,但是 angular 有一个$anchorScroll服务。至于重新加载数据,您可以使用$cacheFactory缓存它,或者将数据存储在更高的范围内。

我创建了一个适用于窗口滚动的指令(它可以更新为适用于任何元素)

html 用法

<div ng-keep-scroll="service.scrollY">
<!-- list of scrolling things here -->
</div>

其中“service.scrollY”必须是服务中的变量。服务保留它们的状态和值,控制器在每次加载和清除它们的值时都会重新创建,因此您不能使用它们来存储持久数据。控制器有一个指向服务的作用域变量。

指令js

app.directive('ngKeepScroll', function ($timeout) {
    return function (scope, element, attrs) {

        //load scroll position after everything has rendered
        $timeout(function () {
            var scrollY = parseInt(scope.$eval(attrs.ngKeepScroll));
            $(window).scrollTop(scrollY ? scrollY : 0);
        }, 0);

        //save scroll position on change
        scope.$on("$routeChangeStart", function () {
            scope.$eval(attrs.ngKeepScroll + " = " + $(window).scrollTop());
        });
    }
});
服务示例: mod.factory("CacheService", function($cacheFactory) { return { data:{ scrollY: 0 } }; }); 然后在控制器中: $scope.service = CacheService.data
2021-05-15 13:40:34