programing

AngularJS에서 경로 변경 시 스크롤 위치를 유지하시겠습니까?

css3 2023. 3. 6. 21:21

AngularJS에서 경로 변경 시 스크롤 위치를 유지하시겠습니까?

샘플 앱: http://angular.github.com/angular-phonecat/step-11/app/ #/phones

마지막 전화 '모토로라 매력'을 선택하면 휴대폰의 상세 내용이 표시됩니다.브라우저에서 를 사용하여 다시 이동하면 데이터가 새로고침되고 스크롤이 맨 위에 표시됩니다.

되돌아갔을 때 남아있던 위치까지 자동으로 스크롤하는 가장 좋은 방법은 무엇입니까?또한 각도가 데이터를 새로고침하는 이유는 무엇입니까?

컴퓨터에 동일한 "Angular-phonecat" 샘플이 있으며 스크롤할 때마다 데이터를 로드하는 무한 스크롤을 추가했습니다.따라서 사용자가 50개 이상의 항목을 다시 로드하거나 30초 동안 아래로 스크롤하지 않았으면 합니다.

상세 보기 후 목록 보기에서 스크롤 위치를 복원하는 방법을 보여 주는 바이올린이 있습니다. 아직 지침으로 캡슐화되지 않았습니다. 작업 중...

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' 외부에서 목록을 한 번 가져오는 것도 표시됩니다.

다음으로 keep-scroll-pos 디렉티브의 다른 버전을 나타냅니다.이 버전

  • $routeProvider 정의의 각 templateUrl 스크롤 위치를 기억합니다.

  • 해시 태그(예: #/home#section-2)는 이전 스크롤 위치가 아닌 #section-2로 스크롤합니다.

  • 자체 완결형으로 사용하기 쉬우며 스크롤 위치를 내부에 저장합니다.

html 사용 예:

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

keep Scroll Pos 디렉티브의 코드는 다음과 같습니다.

"엄밀하게 사용";
angular.module("myApp.directives", [])
.directive("keepScrollPos", 함수($route, $window, $timeout, $location, $anchorScroll) {
// 각 경로의 templateUrl 캐시 스크롤 위치var 스크롤PosCache = {};
// 컴파일 함수return 함수(실행, 요소, 특성) {
범위.$on('$route Change Start', function() {// 현재 보기에 대한 스크롤 위치 저장($route.current)의 경우스크롤 Pos Cache [$route.current.loaded]TemplateUrl] = [$window.pageXOffset, $138.page]YOffset ];}});
범위.$on('$route Change Success'), function() {// 해시가 명시적으로 지정된 경우 이전에 저장된 스크롤 위치보다 우선합니다.($location.filength()) {$anchorScroll();
// 그렇지 않으면 이전 스크롤 위치를 가져옵니다. 없는 경우 페이지 맨 위로 스크롤합니다.} 기타 {var prevScrollPos = scrollPosCache[$route.current.loaded]TemplateUrl] || [ 0 , 0 ] ;$syslog(function) {$window.scrollTo(prevScrollPos[0], prevScrollPos[1]);}, 0);}});}});

이전에 저장된 스크롤 위치를 무시하고 강제로 맨 위로 스크롤하려면 #top, 예를 들어 href="#/home#top"과 같은 유사 해시 태그를 사용합니다.

또는 항상 맨 위로 스크롤하는 경우 내장 ng-view 자동 스크롤 옵션을 사용합니다.

<div ng-view autoscroll></div>

디렉티브를 작성하기 위해 @Joseph Oster의 솔루션을 사용하고 있습니다.또, 이하의 회답도 갱신해 사용할 수 있습니다.

  • $location Change Start
  • $location Change Success

다른 이벤트는 사용되지 않기 때문입니다.

Findle은 이쪽: 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;
            });
        }
    };
})

전에 사용한 적은 없지만, angular에는 $anchorScroll 서비스가 있습니다.데이터 새로고침에 대해서는 $cacheFactory를 사용하여 캐시하거나 더 높은 범위에 데이터를 저장할 수 있습니다.

윈도우 스크롤에서 동작하는 디렉티브를 작성했습니다(단, 어떤 요소에서도 동작하도록 갱신할 수 있습니다).

html 사용방법

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

여기서 "service.display"는Y"는 서비스 내의 변수여야 합니다.서비스는 상태와 값을 유지합니다.컨트롤러는 값을 로드하고 클리어할 때마다 재생성되므로 영속적인 데이터를 저장하는 데 사용할 수 없습니다.컨트롤러에는 서비스를 가리키는 스코프 변수가 있습니다.

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

br2000의 훌륭한 답변을 바탕으로 UI-router와 연동하도록 디렉티브 코드를 업데이트했습니다. $하여 $state.의 하나의 를 만듭니다.scrollPosCache★★★★★★ 。

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

    // cache scroll position of each route's templateUrl
    var scrollPosCache = {};

    // compile function
    return function(scope, element, attrs) {

      scope.$on('$stateChangeStart', function() {
        // store scroll position for the current view
        if ($state.current.name) {
          scrollPosCache[$state.current.name + JSON.stringify($state.params)] = [ $window.pageXOffset, $window.pageYOffset ];
        }
      });

      scope.$on('$stateChangeSuccess', function() {
        // if hash is specified explicitly, it trumps previously stored scroll position
        if ($location.hash()) {
          $anchorScroll();

          // else get previous scroll position; if none, scroll to the top of the page
        } else {
          var prevScrollPos = scrollPosCache[$state.current.name + JSON.stringify($state.params)] || [ 0, 0 ];
          $timeout(function() {
            $window.scrollTo(prevScrollPos[0], prevScrollPos[1]);
          }, 0);
        }
      });
    }
  })

페이지에 표시하기 위해 데이터 가져오기가 필요한 경우 $routeChangeSuccess를 사용하여 스크롤 함수 호출을 지연해야 할 수 있습니다.

    scope.$on("$routeChangeSuccess", function() {
        $timeout(function () {
            var scrollY = parseInt(scope.$eval(attrs.ngKeepScroll));
            $(window).scrollTop(scrollY ? scrollY : 0);
        }, 1000); // delay by 1 sec
    });

문서 본문뿐만 아니라 오버플로우된 요소에서도 사용할 수 있는 버전을 만들었습니다.

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

  // cache scroll position of each route's templateUrl
  var cache = {};

  return {
    restrict : 'A',
    link: function($scope, elements, attrs){

      $scope.$on('$routeChangeStart', function() {

        // store scroll position for the current view
        if($route.current)
          cache[$route.current.loadedTemplateUrl + ':' + attrs.keepScrollPos] = [elements[0].scrollLeft, elements[0].scrollTop];              

      });

      $scope.$on('$routeChangeSuccess', function(){
        // if hash is specified explicitly, it trumps previously stored scroll position
        if($location.hash()){
          $anchorScroll();
          return;
        }

        // else get previous scroll position and apply it if it exists
        var pos = cache[$route.current.loadedTemplateUrl + ':' + attrs.keepScrollPos];
        if(!pos)
          return;

        $timeout(function(){                  
          elements[0].scrollLeft = pos[0];
          elements[0].scrollTop = pos[1];            
        }, 0);

      });

    }
  }

})

사용방법:

<div keep-scroll-pos="some-identifier"> ... </div>

이 문제를 해결할 수 있는 다른 간단한 방법을 찾았습니다.

var scrollValue = $(window).scrollTop();

$rootScope.$on("$routeChangeStart", function() {
    scrollValue = $(window).scrollTop();
});

$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    setTimeout(function() { $(window).scrollTop(scrollValue); }, 0);
});

.run()에 넣습니다.

이와 같이 타임아웃 값을 0으로 설정해도 정상적으로 동작하지만 페이지 렌더링 후에 실행됩니다(타임아웃 기능이 없으면 내용 렌더링 전에 실행되므로(템플릿 또는 데이터 로드) 기능은 사용할 수 없습니다).

일부 API에서 데이터를 가져오는 경우 $rootScope 함수로 타임아웃을 랩하고 요청이 성공한 후 실행할 수 있습니다.

루트 변경 시마다 스크롤 위치를 리셋해야 합니다.기본 AppController에서 다음을 사용합니다.

  $scope.$on("$routeChangeSuccess", function () {
    $anchorScroll();
  });

또는 ui-route를 사용하는 경우:

  $scope.$on("$stateChangeSuccess", function () {
    $anchorScroll();
  });

자세한 내용은 AngularJS에서 URL 해시에 $watch를 추가하려면 어떻게 해야 합니까?

할 수 거야, 가 있어.$httpProvider.defaults.cache = true;

다른 답들과 달리, 나는 단지 두루마리 이상의 것을 기억하고 싶었다.input 들 fieldvalues.

그뿐만 아니라, 많은 사람들은 이 사람들이

  • 하나의 스크롤 요소(페인이 있거나 다른 앱과 같은 디스플레이가 있을 수 있음)만 기억하고 싶었을 때,
  • 있다body스크롤 요소로 사용합니다(: 각도 스냅을 사용하는 경우).
  • (즉, 앵귤러로 .ng-view를 참조해 주세요.
<body> <!-- doesn't scroll -->
    <div snap-drawers>..</div>

    <div snap-content="" history="scrollTop"> <!-- the scrolling div-->
        <header>...</header>

        <div ng-view>
            <input name="email" history="val"> <!-- tag with value we want remembered -->

            <div history="scrollLeft" history-watch="scroll" id="evenHorizontalScroll"><!--
                custom value we want remembered.
                NB it must have an id to be identified after angular
                removes it from the DOM between views,
                and since it is not a recognised default we can tell my
                directive the jquery event function what to watch
            --></div>
        </div>
    </div>
</body>

저는 이러한 문제를 해결하는 공유 범위 지침을 작성했습니다.

.directive('history', function($compile, $rootScope, $location) {
    return {
        restrict : 'A',
        replace : false,
        scope : false,

        controller : function($scope, $timeout) {
            //holds all the visited views
            var states = new Object();
            //the current view
            var state = null;
            //how many names have been generated where the element itself was used
            var generated = 0;

            //logs events if allowed
            function debug(from) {
                //comment this to watch it working
                //return;

                console.log('StateHistory: ' + from);
                if (from == 'went')
                    console.log(state);
            }

            //applies the remembered state
            function apply() {
                var element;
                //for each item remembered in the state
                for (var query in state) {
                    //use the element directly, otherwise search for it
                    (state[query].element || $(query))
                        //use the appropriate function
                        [state[query].property](
                            //and set the value
                            state[query].value
                        )
                    ;
                    debug('applying:' + query + ':' + state[query].value);
                }

                //start recording what the user does from this point onward
                $scope.ignore = false;
            }

            //generates a reference we can use as a map key
            $scope.generateRef = function() {
                return '' + (++generated);
            };

            //views changed
            $scope.went = function() {
                debug('went');

                //set the current state
                state = states[$location.path()];

                //if we dont remember the state of the page for this view
                if (!state)
                    //get recording!
                    state = states[$location.path()] = new Object();

                //apply the state after other directives
                //(like anchorScroll + autoscroll) have done their thing
                $timeout(apply);
            };

            //one of the elements we're watching has changed
            $scope.changed = function(name, element, property, useObject) {
                //if we're not meant to be watching right now
                //i.e. if the user isnt the one changing it
                if ($scope.ignore) {
                    debug('ignored');
                    return;
                }

                //if we havent recorded anything for this here yet
                if (!state[name]) {
                    //start recording
                    state[name] = {property:property};

                    //and remember to leave behind a reference if the name isn't
                    //good enough (was generated)
                    if (useObject)
                        state[name].element = element;
                }

                //use the requested function to pull the value
                state[name].value = element[property]();

                debug('changed:' + name + ':' + state[name].value);
            };

            //initial view
            $scope.went();

            //subsequent views
            $rootScope.$on('$routeChangeSuccess', $scope.went);
            $rootScope.$on('$routeChangeError', $scope.went);

            $rootScope.$on('$routeChangeStart', function() {
                debug('ignoring');
                $scope.ignore = true;
            });
        },

        link: function (scope, element, attrs) {
            //jquery event function name
            var watch = attrs.historyWatch;
            //if not set, use these defaults
            if (!watch) {
                switch (attrs.history) {
                case 'val':
                    watch = 'change';
                    break;
                case 'scrollTop':
                    watch = 'scroll';
                    break;
                default:
                    watch = attrs.history;
                }
            }

            //the css selector to re-find the element on view change
            var query = null;
            //the reference to the state remembered
            var name;

            //try using the id
            if (attrs.id)
                name = query = '#' + attrs.id;
            //try using the form name
            else if (attrs.name)
                name = query = '[name=' + attrs.name + ']';
            //otherwise we'll need to just reference the element directly
            //NB should only be used for elements not swapped out by angular on view change,
            //ie nothing within the view. Eg the view itself, to remember scrolling?
            else
                name = scope.generateRef();

            //jquery value function name
            var property = attrs.history;

            //watch this element from here on out
            element.on(watch, function() {
                scope.changed(name, element, property, !query);
            });
        }
    };
})

나는 내 프로젝트에서 커스텀 솔루션을 사용하고 있다.

1단계: 목록에서 클릭 위치를 가져와 로컬 저장소에 저장합니다.

var position = document.body.scrollTop;
localStorage.setItem("scrollPosition",position);

스텝 2: 상세 뷰에서 글로벌 변수 backFromDetailView를 true로 설정합니다.

backFromDetailView = true;

3단계: 상세 보기 페이지에서 목록으로 돌아가는 경우.모든 컨텐츠가 다시 서버에서 스크롤된 위치까지 다시 로드됩니다.

이를 위해 다음 행을 사용하여 함수를 html로 바인드합니다.

컨트롤러에는 다음과 같은 기능이 있습니다.

$scope.goto = function (){
    if(backFromDetailView){
         window.scrollTo(0, localStorage.getItem("scrollPosition"));
     }
}

이 기술의 몇 가지 단점은 다음과 같습니다.

  1. 추가 콘텐츠를 포함한 모든 콘텐츠가 다시 로드됩니다.

  2. iOS 에서는, 적절한 위치로 스크롤 하기 전에 검은 화면이 표시됩니다.

@br2000의 뛰어난 솔루션.

그러나 안타깝게도 제가 스크롤을 되돌리고 있던 페이지는 지시문이 위치를 복원하려고 할 때 백엔드에서 긴 목록으로 데이터를 로드하고 있었습니다.

스크롤 위치를 복원하지 못한 것이 분명합니다.가 를사사 it using using using using using using를 사용해서 풀었어요.$interval$timeout하고 20번 300ms timeout했습니다.에서 돌아온 약속을 저장했습니다.$interval 안에 것을 했습니다.$interval, 나는 스코프 메서드를 한다.$interval - $interval.cancel(promise).

의 가가가 addition addition addition addition addition additionpageYOffset ★★★★★★★★★★★★★★★★★」pageXOffset이었습니다. 0이니까요.왜냐하면overflow-x: hidden되었습니다.div DOM요.div 사람의 div이치

emp의 답변에 동의했지만 angular ui-temp > = 버전 1.0.0(현재 1.0.3)을 사용하고 있는 사용자는 ui-temp의 새로운 전환을 사용하여 그의 지시문을 다시 작성했는지 확인하십시오.

HTML

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

각도 지시

angular.module("app")
    .directive("keepScrollPos", function($transitions, $state, $window, $timeout, $location, $anchorScroll) {

        // cache scroll position of each state's templateUrl
        var scrollPosCache = {};

        return {
            link: function(scope, element, attrs) {


                $transitions.onStart({ }, function( trans ) {

                    // store scroll position for the current view
                    if (trans.from().name) {
                        scrollPosCache[trans.from().templateUrl] = [ $window.pageXOffset, $window.pageYOffset ];
                    }

                    trans.promise.finally(function () {


                        // if hash is specified explicitly, it trumps previously stored scroll position
                        if ($location.hash()) {
                            $anchorScroll();

                        // else get previous scroll position; if none, scroll to the top of the page
                        } else {
                            var prevScrollPos = scrollPosCache[trans.to().templateUrl] || [ 0, 0 ];
                            $timeout(function() {
                                $window.scrollTo(prevScrollPos[0], prevScrollPos[1]);
                            }, 200);
                        }
                    });
                });
            }
        }
    });

언급URL : https://stackoverflow.com/questions/14107531/retain-scroll-position-on-route-change-in-angularjs