[whatwg] R: Proposal: Allow disabling of default scroll restoration behavior

On Mon, Apr 27, 2015 at 9:54 PM Jonas Sicking <jonas@sicking.cc> wrote:

> Jumping in at the end here.
>
> As I've said before, I like the general idea of giving pages more
> control over scroll restoration, but I don't think we should tie this
> to pushState()/replaceState()/onscroll.
>
My proposal is instead that we add an API like
>
> history.restoreScroll = boolean;
>

Interesting. I believe Simon has also proposed a similar API for which you
can find my original objections here
<https://lists.w3.org/Archives/Public/public-whatwg-archive/2015Apr/0045.html>
.

One of my objections was that any proposed API should give developers
control of scroll restoration per individual history entry. Initially I
assumed a single boolean flag cannot provide that control but now I believe
what you are proposing here can in fact provide per-entry control. More on
this later.

My other objection was that because scroll restoration behaviour is
ultimately tied to a specific history entry, the API to control it should
reflect this underlying fact. IMO a flag on history object hides this fact
while original proposal does not.

I understand that for pages that do not create any state other than their
default initial state (e.g., infinite scrollers) setting
history.restoreScroll is simpler than using
history.replaceState({restorScroll: false}). But I think this additional
complexity is not prohibitive and can be justified when the upside is an
API that better explains the underlying behaviour and is simpler for pages
that do create multiple states using history.pushState.

This property would default to true. Whenever pushState() is called,
> or the user navigates away from the current page, for example by
> clicking a <a href=...> the value of history.restoreScroll is copied
> into the session-history-entry data that the browser keeps internally.
> As soon as the new session-history-entry is created, restoreScroll is
> set to true again.
>

So history.restoreScroll is copied into current history entry anytime a new
history entry is created (or replaced). If my reading is correct then this
makes sense and it can give per history entry control. The value of
history.restoreScroll is reset to its default when a new document is loaded.

Consider this case where page A uses pushState once and then navigates to
page B. This creates three history entries: 2 for A and one for B.

A  -->  Ai  (*)   ==> B
where:
-->  pushState
==> navigation to new page
(*) history.restoreScroll is set to 'false'

This is what I think should happen:
on A load:  history.restoreScroll is reset to default value: TRUE.
                  history entry for A is created.

on -->:        current entry (A) is updated with current restoreScroll: TRUE
                   history entry for Ai is created

on (*):          history.restoreScroll is set to 'FALSE'.

on ==>:        current entry (Ai) is updated with current restoreScroll:
FALSE

on B load:  history.restoreScroll is reset to default value: TRUE
                   history entry for B is created.

A gets TRUE
Ai gets FALSE
B gets TRUE

Additionally we could enable passing a boolean to pushState(), and
> this value would be used as the new initial value for restoreScroll.
> So something like pushState({ url: myURL, restoreScroll: false });.
> This would simply be syntax sugar for |pushState("", "", myURL);
> history.restoreScroll = false;|.
>

It is a nice syntax sugar but I don't feel it adds enough value to justify
doing this at browser level. It can be done in Javascript by
framework/poly-fill authors.


> I think that's all that's needed.
>

Yes. Both APIs are equally expressive and one may be built on top of the
other.

I suggest two minor changes though:

1. We specifically proposed using a dictionary for options with scroll
restoration being one of its parameters. This allows us to add additional
history entry control parameters in the future. For example we can expose
zoom restoration similarly if there is enough demand.
2. As suggested earlier in the thread it is better to use a name that
suggests more strongly that the author is expected to restore the position
themselves.

So:

history.options = {
   willRestoreScroll =  false
}

In order to make it easier for pages we could also expose a
> history.restoredScrollPosition which is a readonly property which the
> UA writes to any time it restores a session history entry, which is
> the scroll position that it would have restored scrolling to if the
> page hadn't disabled scroll restoration for the given session history
> entry.
>
This is isn't strictly needed though since the page can simply use
> sessionStorage and update the scroll position in a onscroll handler.
>

Storing position is sessionStorage works well. It has the advantage that
the the same logic can handle scroll restoration for both history and
non-history navigations (e.g., same page being loaded as a result of
clicking back button or a link).

Providing scroll position is nice to have but not required. I think it
requires additional spec work to exactly define the behaviour of scroll
restoration across different browsers. Right now different vendors have
different behaviour when it comes to restoring scroll position for iframes,
and inner scrollables etc. So it is not really clear how exactly this
'scroll position' should look like.

Getting this boolean flag should not be blocked on providing scroll
position.

But history.restoredScrollPosition would reduce boilerplate code.
> Also, using sessionStorage is somewhat complex to do correctly given
> that the user might visit the same URL multiple times in the same
> session.

(As an aside, it would be great if we had something like
> sessionStorage, but specific for a given page. Right now
> sessionStorage seems to make it very hard to store page specific state
> unless I'm missing something obvious?)


Using sessionStorage for page storage requires that developers be aware
that it is shared by the whole domain in order to avoid making certain
mistakes. A per page storage makes things simpler but I am not sure about
its implications.

Majid


> / Jonas
>
>
> On Thu, Apr 23, 2015 at 3:25 PM, Majid Valipour <majidvp@chromium.org>
> wrote:
> > On Tue, Apr 21, 2015 at 4:58 PM Ian Hickson <ian@hixie.ch> wrote:
> >
> >> On Sun, 12 Apr 2015, Anne van Kesteren wrote:
> >> > On Thu, Apr 9, 2015 at 9:05 PM, Ian Hickson <ian@hixie.ch> wrote:
> >> > > I'd strongly recommend against adding new methods. It'll mean we now
> >> > > have two different ways to do the same thing, which means more bugs,
> >> > > which means less interoperability, more confusing behaviour for
> >> > > authors, more to document, etc.
> >> >
> >> > If the existing method didn't have the flaw with the title argument I
> >> > wouldn't have suggested it. Also, since they both built upon the same
> >> > primitive I think we'd be okay in the bugs and interop department.
> >>
> >> You are more optimistic than I. In any case, I strongly recommend
> against
> >> such redundancy.
> >>
> >>
> >> On Wed, 15 Apr 2015, Majid Valipour wrote:
> >> >
> >> > Actually URL is optional in current spec and it defaults to current
> URL.
> >> > Why is this suboptimal?
> >>
> >> Because it means you can't bookmark the state or share the state,
> >> reloading the page loses the state, etc.
> >>
> >>
> >> > In anycase If making URL required is a goal then it is best done by
> >> > introducing a new method to avoid breaking compatibility.
> >>
> >> Why is that better?
> >>
> >> Changing the optional third argument to become required on the existing
> > methods will break any call site that is not passing it. This is a non
> > trivial compatibility issue which does not exists with a new method.
> >
> >
> >>
> >> > I personally find a dictionary with only optional members which have
> >> > appropriate defaults to be very convenient.
> >>
> >> I don't disagree... for new APIs. But when we already have an existing
> >> API, maintaining consistency and lack of redundancy IMHO trumps pretty
> >> much everything else, if you want the end result to be usable.
> >>
> >> A lot of the pain with using the Web's APIs is the inconsistency and
> >> redundancy that is rampant throughout.
> >>
> >
> > I understand the desire for maintaining consistency and reducing
> > redundancy. On the other hand a new API will allow fixing some existing
> > warts. I can see merits in both arguments. I am happy to defer the API
> > decision to spec editors.
> >
> > I created the W3C bug for this proposal:
> > https://www.w3.org/Bugs/Public/show_bug.cgi?id=28553
> >
> > Majid
>

Received on Tuesday, 28 April 2015 16:20:18 UTC