[whatwg] History API, pushState(), and related feedback

Ian Hickson wrote:
> On Fri, 22 Jan 2010, Mike Wilson wrote:
> > 
> > I'll keep this short as there is more recent discussion:
> > 2) The pageStorage object is one incarnation of [a key
> >    value store] solving the dependency problem that appears
> >    when different components want to save data to the single
> >    session history state object
> 
> I'm really not convinced this is a problem we need to solve. 
> There are plenty of places where the Web platform doesn't 
> cater for different parts of a page being written by 
> different people who aren't coordinating. The 
> whole DOM, for instance. Event handler attributes. All the 
> storage APIs. 

I think the storage APIs (localStorage and sessionStorage) are
the better comparison of these as they are also state handling
mechanisms. These do allow selected parts of the state to
be accessed individually as they are key/value stores. Thus
they allow different parties to have uncoordinated access to 
their own data.

Fwiw, with some good will you can say that cookies are also 
handled as key/value stores as their values are scoped on both 
domain and key (cookie name). This allows different parts of
the code to deal with their own values as long as there isn't
a key (cookie name) collision.

> > And the later part is more about general properties of
> > API design:
> > 3) If a key-value store is desired, then using the same API 
> >    as the other key-value stores is a strength and not an over-
> >    generalisation. The web doesn't need yet another API.
> > 4) Thinking about possible future additions when choosing
> >    names is one part (of many) of a successful design.
> 
> I don't understand the above points.

The main point is: Why not use the same API as in Web Storage:
  interface Storage {
    readonly attribute unsigned long length;
    getter DOMString key(in unsigned long index);
    getter any getItem(in DOMString key);
    setter creator void setItem(in DOMString key, in any data);
    deleter void removeItem(in DOMString key);
    void clear();
  };
and make the current entry's Storage instance always available
as f ex:
  interface History {
    readonly attribute Storage state;
  }

Then pushState's state parameter may be removed and there is
no longer a need for the replaceState method.

Apart from API harmonization this also fixes the differences
in semantic contract. With this I mean that when Justin Lebar 
and I started this discussion last summer the pushState API 
was still very restrictive and only allowed retrieval of data 
during the popstate event and only allowed storage of data 
when pushState:ing a new history entry. 
Making an analogy with sessionStorage would mean that it would 
only be allowed to read data from sessionStorage during the 
load event and only be allowed to write data at the same time 
as assigning window.location.

Since then we have seen some changes to the spec and now the 
state object may be written to at any time, and may also be 
written to without creating a new history entry. Reading from 
it at any time is still missing, for what reason I don't know?

Nevertheless, the semantic contract is coming closer and 
closer to that of the other storage APIs, so I think it
would be an advantage to use the same interface as well.

> > [...] Ie, this data is persisted on demand at a certain 
> > point in the history entry's life cycle, just as I am 
> > suggesting for the pushState state.
> >
> > With the same reasoning as for current pushState, the spec 
> > would instead suggest that scroll position and form control 
> > values were persisted immediately when changed, instead of 
> > at the "leave history entry" event.
> 
> The problem is that you really want the URL to always be up 
> to date, so that the user can copy it. And thus you really 
> want to be calling pushState() whenever the state changes.

Indeed a fair bit of cases should reflect their state in
the URL, but, referring to my earlier examples, state like
scroll position and form control values usually should not.
I'm talking about this latter kind of data.

> back/forward is a UI state navigation, not a data state 
> navigation.

We are in agreement that back/forward is not a generic undo/
redo mechanism. Still, UI state may have a broad definition.

> Hitting "back" would take the user into a previous state in 
> the application, it wouldn't affect the user's work.

Yes and no. According to the current spec
http://dev.w3.org/html5/spec/Overview.html#history-traversal
user agents are free to persist scroll position and form 
control values on history traversal. Naively I'd consider
scroll position to be UI state and form control values to be
user data state, and both seem to be eligible for persisting
on history entries.

So, I think there may be cases where it is desired to 
(temporarily) store the contents of a rich-text form field
on a history state, or maybe store complex UI state such as 
the open/close status of a large number of tree nodes, both
of which would be overkill to replaceState on every change.

The only thing needed to support this in an efficient way
is to provide something like a "beforepopstate" event that
fires before the new history entry is made current, so that
a call to replaceState will still target the "old" entry.

Best regards
Mike

Received on Thursday, 18 February 2010 09:45:55 UTC