【问题标题】:Retaining Scroll Position in Ember在 Ember 中保留滚动位置
【发布时间】:2014-11-06 16:35:09
【问题描述】:

我正在尝试创建一个 Ember 应用,其中保留路线之间的滚动位置,为用户在浏览时提供更愉快的体验。

预期结果

如果我在 /pageb 上向下滚动 500px,单击指向 /pagea 的链接,单击浏览器后退按钮,我应该回到 500px 滚动顶部。

实际结果

点击浏览器后退按钮时,滚动顶部等于/pagea的高度。

如何重现

git clone https://github.com/eTilbudsavis/ember-scroll-demo
cd ember-scroll-demo
npm install
ember serve
open http://localhost:4200/pageb

然后你一直滚动到底部,点击pagea链接,然后点击浏览器的返回按钮,你应该会看到问题。

结语

我认为这种行为的原因是当我单击浏览器后退按钮时,浏览器会尝试滚动回它在离开该路线之前的位置。因为 Ember 还没有渲染那个视图,所以浏览器不能去那里。它只能向下滚动到 /pagea 的高度。

现在,更棘手的是,如果我转到浏览器的控制台并在单击返回后手动设置滚动顶部,也没有任何反应。浏览器似乎认为它位于右侧滚动顶部,即使它不是。如果我用鼠标进行单次滚动,浏览器会一直跳到它应该在的位置。

我希望我已经尽可能简洁地解释了这一点。否则,请随时询问更多详细信息。

谢谢!

【问题讨论】:

  • 我更新了我的答案,它现在应该可以在 Firefox 和 Chrome 浏览器中使用了。
  • 另一个答案,带有混合:reefpoints.dockyard.com/2014/05/05/…
  • 我试过那个,但它并不能正常工作,尤其是在 Chrome 中。

标签: jquery css ember.js pushstate


【解决方案1】:

您似乎试图依赖浏览器的“滚动状态”,但您没有指定您使用的浏览器。

我尝试创建长页面并转到外部链接,当前Chrome在返回后保存了滚动状态,但Firefox没有。

因此,不依赖浏览器,“状态保存”问题的一般答案是使用queryParams

我创建了将滚动状态保存到 queryParams 的示例。请注意,我使用replaceState 来防止浏览器为每次滚动创建新的历史记录条目。

App = Ember.Application.create({});

App.ApplicationRoute = Ember.Route.extend({
  queryParams: {
    scrollTop: {
      replace: true
    }
  },
  setupController: function(controller) {
    $(window).on('load', function() {
      $(document).scrollTop(controller.get('scrollTop'));  
    });
  }
});

App.ApplicationController = Ember.Controller.extend({
  queryParams: ["scrollTop"],
  scrollTop: 0,
  handleScroll: function() {
    this.set('scrollTop', $(document).scrollTop());
  },
  bindScrollEvent: function() {
    $(window).on('scroll', Ember.run.bind(this, this.handleScroll));
  }.on('init')
});

JSBin 是here

更新:我用固定版本替换了代码示例,它实际上适用于 Firefox 和 Chrome。

【讨论】:

  • 感谢您的回答。我很感激!但是,我有点担心在 URL 中保存滚动状态。我想不出有什么网站可以做到这一点? IMO 在每次滚动时都更新 URL 有点令人困惑。自从提出我的问题以来,我一直在研究一个潜在的解决方案:gist.github.com/anonymous/b51b25ec8d1a36cba752。它在 CoffeeScript 中——我希望你不介意。它似乎适用于所有浏览器。你怎么看?
  • 看起来不错,但是,我还没有从要点中理解,滚动状态保存在哪里(会话,localStorage)?如果您想将带有存储滚动状态的 URL 传递给其他人,那么存储在 URL 中会很有用。我还没有看到任何实际这样做的网站。你可能是第一个 :) 实际上,没有 Ember 很难做到。
  • 我实际上不想在访问外部资源后返回网站时保存滚动状态。我想确保网站体验记住每条前进和后退路线的滚动位置。
【解决方案2】:

如果有人想使用这个路由 mixin,我自己已经解决了这个问题:

`import Ember from 'ember'`

Ember.$(window).on 'unload', ->
    $(@).scrollTop 0

    return

PreserveScrollMixin = Ember.Mixin.create
    # @property [Integer] the time to wait before running the scroll callback
    #
    scrollingTimeout: 100

    # As soon as the route initializes, set the min height on the body and the scroll top on the window and listen to
    # future scroll events.
    #
    beforeModel: ->
        height = @getWithDefault 'controller.height', 0
        scrollTop = @getWithDefault 'controller.scrollTop', 0
        onScroll = =>
            Ember.run.debounce @, @runScrolled, @scrollingTimeout

            return

        Ember.$('body').css 'min-height', height
        Ember.$(window).on 'scroll.scrollable', onScroll
        Ember.$(window).scrollTop scrollTop

        return

    # When the route's model is ready, set the scroll top again to be sure.
    #
    afterModel: ->
        scrollTop = @getWithDefault 'controller.scrollTop', 0

        Ember.run.next @, ->
            Ember.$(window).scrollTop scrollTop

            return

        return

    # Sets the scroll top for the window.
    #
    runScrolled: ->
        @set 'controller.scrollTop', Ember.$(window).scrollTop()

        return

    actions:
        # When the route appears, allow the body to flow naturally.
        #
        didTransition: ->
            Ember.$('body').css 'min-height', '100%'

            return

        # When the route is about to leave, stop listening to scroll events and set the current body height.
        #
        willTransition: ->
            Ember.$(window).off 'scroll.scrollable'
            @set 'controller.height', Ember.$('body').height()

            return

`export default PreserveScrollMixin`

【讨论】:

  • 现在您将滚动状态存储在控制器中。如果在整个应用程序中多次使用控制器,则只会出现一种滚动状态,对吧?
  • 我实际上已经对其进行了一些更改,以便滚动状态不在控制器内部。相反,它位于一个名为 scrollTops 的全局(模块)对象中,我在其中持续更改如下:scrollTops[@routeName] = Ember.$(window).scrollTop()。
  • 同样的问题仍然存在,因为您现在可以为每条路线存储一个滚动状态,而不是每个历史项目单独的滚动状态。
猜你喜欢
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-06
  • 2011-10-22
  • 2019-05-20
相关资源
最近更新 更多