Angular ui-router 滚动到顶部,而不是 ui-view

Posted

技术标签:

【中文标题】Angular ui-router 滚动到顶部,而不是 ui-view【英文标题】:Angular ui-router scroll to top, not to ui-view 【发布时间】:2014-04-12 23:45:15 【问题描述】:

我刚刚从0.2.0 升级到ui-router 0.2.8,我注意到当状态发生变化时,滚动位置会跳到子ui-view 的顶部,这是新状态的主题。

这很好,但我有两个问题:

1) 我在页面顶部和ui-view 之间有 30 像素的填充,我希望它滚动到页面顶部,留下一个空隙。目前它正好到达了看起来很丑的ui-view 的顶部。为了实现这一点,我想我要么需要知道如何让它滚动到 ui-view 所在的 div 的顶部(而不是浏览器 viewport),要么我需要了解如何覆盖 $uiViewScroll滚动到ui-view - 30px。

我尝试过$uiViewScrollProvider.useAnchorScroll();,但如果我这样做,它根本不会滚动。我也试过<ui-view autoscroll="false">;,它也完全停止滚动。

2) 它现在实际上并没有滚动,只是跳跃。是假设滚动还是由开发人员使用 CSS 过渡来完成?

任何帮助将不胜感激:)

【问题讨论】:

你能提供一个现场演示吗? 如果不让我的整个应用程序进入 plunker,非常困难。我会尝试,但不确定如何简化它。 好吧,简化它可能是解决问题的第一步。它将帮助您更清楚地掌握事物。 【参考方案1】:

1) 我认为将autoscroll="false" 放在ui-view 上并在$viewContentLoaded 事件中操作滚动的最简单方法。

2) 这是浏览器在锚点上的默认行为

【讨论】:

我可能可以编写滚动代码,但不确定在我的代码中的哪个位置挂钩到 $viewContentLoaded(我实际上并不知道它存在)【参考方案2】:

当路径更改时,路由器会广播一个事件:$stateChangeSuccess,即 url 已更改,因此只需收听它并使用 jquery 滚动到页面顶部

$rootScope.$on('$stateChangeSuccess',function()
    $("html, body").animate( scrollTop: 0 , 200);
)

把上面的代码放在里面

yourAppName.run(function()

    //the above code here
 )

【讨论】:

这听起来是个不错的建议,虽然它是一个可滚动的 div,我需要转到顶部,而不是 BODY 标签,但我相信我可以解决这个问题。是否可以这样做,以便我可以覆盖某些视图,以便我可以选择不滚动到顶部(例如在页面中间的标签集上)。 我找到了答案,我使用了您的 jquery 代码,但我没有将其放入 $stateChangeSucess 中,而是将其放入控制器中以获取各个视图。这样我就可以决定将它包含在哪些视图中。 使用 jquery??不求助于 jQuery 真的应该是可能的 在这种情况下使用 jquery 是安全的,这就是我的感觉,因为 ui 视图将在 body 标签内,并且 100% 的时间 boy 标签将出现在 dom 中,它只是将继续存在的模板在body标签内改变。所以我觉得使用它很安全! :) @Spock 如果没有动画也可以,可以使用window.scrollTo(0, 0)【参考方案3】:

另一种方法是装饰默认的$uiViewScroll 服务,有效地覆盖默认行为。

app.config(function ($provide) 
  $provide.decorator('$uiViewScroll', function ($delegate) 
    return function (uiViewElement) 
      // var top = uiViewElement.getBoundingClientRect().top;
      // window.scrollTo(0, (top - 30));
      // Or some other custom behaviour...
    ; 
  );
);

正如 Hubrus 所说,对于您不希望申请的任何 <ui-view>,只需添加 autoscroll="false"。我没有仔细研究实际的滚动实现,我只是想我会提到装饰器方式(它很有趣)作为替代方案。我相信您可以计算出确切的滚动行为。

【讨论】:

谢谢,现在我可以访问 uiViewElement 我想我可以访问属性来查看我是否添加了(例如) elementToScrollTo="main-body" 等? 我认为是这样,这不是我自己尝试过的,但听起来可行。我只用它来取消默认滚动行为,因为它对我来说不太好:) 如果它成功了,请告诉我,如果知道这种方法的任何“扭结”会很有趣。祝你好运! @jonhobbs 如果每个状态都可以进入页面顶部,我建议使用 $rootScope.$on('$stateChangeStart', function() $window.scollTo (0,0) );而不是在 $stateParams 上执行手表收集。 uiViewElement 作为数组返回。我能够通过以下方式完成这项工作:var top = uiViewElement[0].getBoundingClientRect().top; 请注意,如果没有滚动条,则不能滚动到 0 以外的位置。因此,您应该将对scrollTo() 的调用包装在$timeout 中。这也是它在原始实现中的完成方式。【参考方案4】:

由于$stateChangeSuccess 在当前的 AngularJS(如 1.2.x)中似乎不再可用,我改变了 Rishul Mattas 以下示例对我来说很好:

app.run(function ($rootScope) 
  $rootScope.$on('$viewContentLoaded',function()
    jQuery('html, body').animate( scrollTop: 0 , 200);
  );
);

【讨论】:

它必须是 $rootScope! 我尝试了您的解决方案,但在我找到这个小原因之前,它几乎让我头疼。感谢更新。【参考方案5】:

所以我遇到了同样的问题。我有一个固定顶部的导航栏。如果我将 autoscroll="true" 放在 ui-view 中,它将滚动到顶部减去滚动条的高度。

所以我摆脱了为顶部导航栏添加填充到正文的样式

// fixed navigation at top
//body  padding-top: 100px; 

并将其应用于 ui-view

[ui-view=main] 
    padding-top: 100px;

现在 autoscroll="true" 按预期工作。

【讨论】:

【参考方案6】:

如果结合了 Angular + Material Design,这也需要滚动到顶部:

app.run(function ($rootScope) 
    $rootScope.$on('$viewContentLoaded', function () 
        $("md-content").animate( scrollTop: 0 , "fast"); /* <------- Notice this line */
        jQuery('html, body').animate( scrollTop: 0 , 200);
    );
);

【讨论】:

【参考方案7】:

如果导航状态是子状态,我认为我们不需要滚动到顶部,所以我写了这个:

$rootScope.$on('$stateChangeSuccess',function(_, _, _, os)
    if(!$state.includes(os) || $state.is(os))
        $("html, body").animate( scrollTop: 0 , 200);
);

【讨论】:

【参考方案8】:

大多数时候,仅仅滚动到页面顶部是不够的。尊重锚链接并滚动到由位置哈希指定的加载内容中的特定位置始终是一个好主意。

这是我用来实现此策略的代码:

module.run(function (
  $rootScope,
  $timeout,
  $location,
  $uiViewScroll
) 

  // Scrolling when screen changed.
  $rootScope.$on('$viewContentLoaded', function () 
    $timeout(performAutoScroll, 0);
  );


  function performAutoScroll () 
    var hash = $location.hash();
    var element =
         findDomElement('#' + hash)
      || findDomElement('a[name="' + hash + '"]')
      || angular.element(window.document.body)
    ;
    $uiViewScroll(element);
  
);

$viewContentLoaded 是 Angular 在 ui-view 元素内加载内容时生成的事件。实际上,我们需要推迟代码的执行,以便将其移至下一个摘要周期。这样视图元素的内容将实际放置在 DOM 树中,因此我们可以对其进行查询。具有零延迟技巧的$timeout 用于此目的。

UI Router 提供的

$uiViewScroll 服务用于实际进行滚动。我们可以传递一个 jQuery/jqLit​​e 元素给它,它会滚动到它的上边框。

我们从$location 服务获取正确的哈希值,并使用它在 DOM 树中找到正确的元素。它可以是具有id 属性的元素或具有name 属性的链接。如果找不到要滚动到的元素,我们会退回到 body 元素(即滚动到页面顶部)。

findDomElement 只是一个语法糖。它是这样定义的:

/**
 * @param string selector
 * @returns jQuery|null
 */
function findDomElement (selector) 
  var result = $(selector);
  return (result.length > 0 ? result : null);

我希望这种方法有意义并且对那里的人有用。干杯!

【讨论】:

【参考方案9】:

置顶

<div id="top">.....

滚动代码:

$rootScope.$on('$stateChangeStart', function() 
    $anchorScroll('top');
);

【讨论】:

使用角原生滚动方法更加简洁。不错!【参考方案10】:

如果您想有条件地对视图执行此操作,您可以将其放置在控制器视图中,如下所示:

.state('exampleState', 
    url: '/exampleURL',
    controller: 'ExampleCtrl as examplectrl',
    onEnter: function($rootScope) 
            $rootScope.$on('$viewContentLoaded',function()
            jQuery('html, body').animate( scrollTop: 0 , 200);
        );
    
).

或可选地,这可能会使您的 state.js 文件更干净,将上述内容放在控制器中以获取给定视图:

function ExampleCtrl ($scope, $rootScope) 

    $rootScope.$on('$viewContentLoaded',function()
            jQuery('html, body').animate( scrollTop: 0 , 200);
        );

希望这对某人有所帮助,如果我遗漏了什么,请告诉我。我使用了后者,对我来说效果很好。

【讨论】:

以上是关于Angular ui-router 滚动到顶部,而不是 ui-view的主要内容,如果未能解决你的问题,请参考以下文章

更改路线不会在新页面中滚动到顶部

Angular 5 在每次路线点击时滚动到顶部

防止在Angular中的单个router.navigate调用上滚动到顶部

Angular 9 在每次更改时滚动到顶部

Angular2 Meteor,实现无限滚动的问题(滚动重置到顶部)

Angular 7在子路由更改上禁用滚动到顶部