在Ember中保留滚动位置

时间:2014-11-06 16:35:09

标签: jquery css ember.js pushstate

我试图创建一个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的高度。

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

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

谢谢!

2 个答案:

答案 0 :(得分:0)

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

我尝试创建长页面并转到外部链接,当前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中使用。

答案 1 :(得分:0)

如果有人想使用它,我已经用这条路线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`