33

我有一个在我的控制器中定义的范围变量 $scope.first_unread_id 。在我的模板中,我有:

<div id="items" >
  <ul class="standard-list">
    <li ng-repeat="item in items" scroll-to-id="first_unread_id">
    <span class="content">{{ item.content }}</span>
    </li>
  </ul>
</div>

我的指令看起来像:

angular.module('ScrollToId', []).
directive('scrollToId', function () {
  return function (scope, element, attributes) {
    var id = scope.$parent[attributes["scrollToId"]];
    if (id === scope.item.id) {
      setTimeout(function () {
        window.scrollTo(0, element[0].offsetTop - 100)
      }, 20);
    }
  }

});

但是,它可以解决两个问题:

  1. 有没有更好的方法将“first_unread_id”从控制器范围中获取到直接而不是询问范围。$ parent?这似乎有点“恶心”。我希望我可以通过视图将其作为参数传递给直接,而不必在任何 li 元素上重复该参数。

  2. 有没有更好的方法来避免 setTimeout() 调用的需要?没有它,它有时会起作用- 我想是由于布局时间的不同。我了解我使用的语法是定义一个链接函数——但我不清楚这是默认情况下的前置链接还是后置链接——以及这是否对我的问题很重要。

4

7 回答 7

39
  1. 您不应该需要 scope.$parent - 因为它将继承父范围的值,并且当它在父范围中发生更改时,它将被传递下来。
  2. 默认为链接后功能。您是否加载了一些图像或某些内容会在初始加载后不久使页面布局发生变化?您是否尝试过没有时间的 setTimeout,例如 setTimeout(function(){})?这将确保这将在其他所有事情完成之后进行。
  3. 我还会稍微更改您指令的逻辑以使其更通用。如果给定条件为真,我会让它滚动到元素。

以下是这 3 个变化:

html:

<div id="items" >
  <ul class="standard-list">
    <li ng-repeat="item in items" scroll-if="item.id == first_unread_id">
      <span class="content">{{ item.content }}</span>
    </li>
  </ul>
</div>

JS:

app.directive('scrollIf', function () {
  return function (scope, element, attributes) {
    setTimeout(function () {
      if (scope.$eval(attributes.scrollIf)) {
        window.scrollTo(0, element[0].offsetTop - 100)
      }
    });
  }
});
于 2012-10-09T00:20:05.700 回答
11

假设父元素是我们滚动的元素,这对我有用:

app.directive('scrollIf', function () {
  return function(scope, element, attrs) {
    scope.$watch(attrs.scrollIf, function(value) {
      if (value) {
        // Scroll to ad.
        var pos = $(element).position().top + $(element).parent().scrollTop();
        $(element).parent().animate({
            scrollTop : pos
        }, 1000);
      }
    });
  }
});
于 2013-12-15T09:00:58.387 回答
6

我最终得到了以下代码(不依赖于 jQ),如果滚动元素不是窗口,它也可以工作。

app.directive('scrollIf', function () {
    var getScrollingParent = function(element) {
        element = element.parentElement;
        while (element) {
            if (element.scrollHeight !== element.clientHeight) {
                return element;
            }
            element = element.parentElement;
        }
        return null;
    };
    return function (scope, element, attrs) {
        scope.$watch(attrs.scrollIf, function(value) {
            if (value) {
                var sp = getScrollingParent(element[0]);
                var topMargin = parseInt(attrs.scrollMarginTop) || 0;
                var bottomMargin = parseInt(attrs.scrollMarginBottom) || 0;
                var elemOffset = element[0].offsetTop;
                var elemHeight = element[0].clientHeight;

                if (elemOffset - topMargin < sp.scrollTop) {
                    sp.scrollTop = elemOffset - topMargin;
                } else if (elemOffset + elemHeight + bottomMargin > sp.scrollTop + sp.clientHeight) {
                    sp.scrollTop = elemOffset + elemHeight + bottomMargin - sp.clientHeight;
                }
            }
        });
    }
});
于 2015-02-06T15:51:34.907 回答
3

与接受的答案相同,但使用 javascript 内置方法“ scrollIntoView ”:

angular.module('main').directive('scrollIf', function() {
    return function(scope, element, attrs) {
        scope.$watch(attrs.scrollIf, function(value) {
            if (value) {
                element[0].scrollIntoView({block: "end", behavior: "smooth"});
            }
        });
    }
});
于 2016-09-02T07:29:24.207 回答
1

结合 UI Router 的$uiViewScroll我最终得到了以下指令:

app.directive('scrollIf', function ($uiViewScroll) {
    return function (scope, element, attrs) {
        scope.$watch(attrs.scrollIf, function(value) {
            if (value) {
                $uiViewScroll(element);
            }
        });
    }
});
于 2015-08-21T18:02:32.593 回答
0

对于在 ES6 中获取最佳答案的答案:

文件:scroll.directive.js

export default function ScrollDirective() {
    return {
        restrict: 'A',
        scope: {
            uiScroll: '='
        },
        link: link
    };

    function link($scope, $element) {
        setTimeout(() => {
            if ($scope.uiScroll) {
                $element[0].scrollIntoView({block: "end", behavior: "smooth"});
            }
        });
    }
}

文件scroll.module.js

import ScrollDirective from './scroll.directive';

export default angular.module('app.components.scroll', [])
    .directive('uiScroll', ScrollDirective);

将其导入项目后,您可以在 html 中使用它:

<div id="items" >
  <ul class="standard-list">
    <li ng-repeat="item in items" ui-scroll="true">
    <span class="content">{{ item.content }}</span>
    </li>
  </ul>
</div>
于 2017-06-26T09:42:35.117 回答
0

结合@uri,这适用于我在.run中使用ui-router和stateChangeSuccess的动态内容:

$rootScope.$on('$stateChangeSuccess',function(newRoute, oldRoute){

        setTimeout(function () {
            var postScroll = $state.params.postTitle;
            var element = $('#'+postScroll);
            var pos = $(element).position().top - 100 + $(element).parent().scrollTop();
            $('body').animate({
                scrollTop : pos
            }, 1000);
        }, 1000);

    });
于 2016-02-10T21:33:22.873 回答