[whatwg] HTML5 History Management

Nathan Hammond nathan at nathanhammond.com
Tue Aug 4 22:18:22 PDT 2009


Ian,
Thanks for the comments. I've simply omitted pieces that I feel like  
are completely addressed. For all of those: Yes, I agree, right,  
right, great, and perfect. Thanks for clarifying and answering silly  
questions. For the rest, see below.

> On Aug 4, 2009, at 9:29 PM, Ian Hickson wrote:
>> On Wed, 29 Jul 2009, Nathan Hammond wrote:
>>

>> Possible Action Items
>> 1. Specify the order in which history related events are triggered.
>>
>> In what order would one expect the events triggered by
>> window.history.pushState({}, "Title", "#newhash")?
>
> No events are triggered by doing that.

With my new understanding of pushState, not triggering popstate makes  
sense. With regards to pushState not triggering a hashchange event, I  
like it, but I do want to be absolutely sure that all implementers get  
this right. So, could we clarify this in the spec? Right now I think  
that the spec could be read where since it adjusts the document's  
current address it might should cause a hashchange event. Providing  
this specific example would do the trick.

Related, is window.location.hash = "newhash"; supposed to trigger a  
hashchange event? (Is this specified? I couldn't find it.) I do  
believe it does in IE8, but I don't have IE8 available to me at this  
moment to check.

>> 2. Specify a method to allow access to the history stack. (At least
>> readable within the context of same-origin, possibly mutable.)
>>
>> This would allow for understanding on the client side of a user's  
>> path
>> through the site, even after navigating away from the page. If this  
>> is
>> not implemented the absolute first thing I will be implementing is a
>> method to track this information, a rather large duplication of  
>> effort
>> for something that could easily be made available. This would  
>> involve a
>> something like currentstate = { _previous: currentstate, title:
>> window.title, newval: true }; plus a form-based storage mechanism to
>> repopulate state in case the user exits on a page which they manually
>> changed the hash to get to (which would not have access to the data
>> object upon revisiting later since there wouldn't be one stored with
>> that history state).
>>
>> I'm aware of the privacy ramifications, but at the same time, I'm  
>> going
>> to be exposing those exact same concerns with the above method.
>
> It turns out to be quite complex to expose this in the API currently,
> because the History object exposes a mix of all the various frames at
> once, and you can get to the History object of cross-origin frames,  
> and
> there's some other legacy baggage here also (e.g. history.length).
>
> Since it's not entirely clear what the use case is, and since you  
> can do
> it to some extent already using onload, onhashchange, and  
> onpopstate, I'd
> rather just let authors reimplement this themselves at this point, and
> maybe revisit this in a future version.

Complexity. Bah! (Then again, you don't have to tell me.) The use case  
I find is pretty simple: in the event that there are two separate ways  
to get to a specific page in the app I need to know how the person got  
there. Or this could be used to provide content tailored specifically  
to that user based upon their history (whether suggestions, ads, or  
help text). Really though, this point is far less critical (as it is  
easy enough to handle in client code) and does greatly increase the  
complexity for getting all implementers on board. If this *isn't*  
implemented however, the point below becomes more critical for being  
able to successfully track state in client code...

>> 3. Specify a method to modify the current history state without  
>> adding a
>> new history point.
>
> Assuming you don't mind causing the page to reload, you can use
> history.replace(). I'm not sure what it would mean to replace the  
> history
> state without changing the Document or anything, though.
>
>> This would alleviate the need for the (incredibly brittle) form-based
>> storage mechanism I describe above.
>
> Can you use sessionStorage for this?

I should have stated this one with a goal: the ability to ensure that  
the popstate event always fires with a full understanding of the (app/ 
page) state when navigating through history. This would be lost when a  
user manually changes the hash. With that as my goal, history.replace  
does not achieve what I am trying to accomplish. Neither does  
pushState without a URL as that still registers a new history point.

Any other techniques for remembering data other than this would still  
be a hack because, in and of itself, the data stored are not uniquely  
tied to a particular history state. In my semi-professional opinion  
form "storage" is really more appropriate for this task as it is  
correctly mapped to the document object on which the data was entered.  
Using sessionStorage I have the additional task of mapping the stored  
series of states to a particular visit of the (app/page) if the user  
visits the site again after navigating away: example.com -> whatwg.com  
-> example.com

(Use case is the game idea I'm toying with that uses state paired with  
CSS3's :target pseudo-class for a sort of battleship meets minesweeper  
thing/demo ... I haven't completely worked out the mechanic but I'm  
planning on turning it into a history demo.)

>> 4. Specify additional properties on the hashchange event.
>>
>> Lots of possible useful information with the number one most  
>> important
>> being the new hash that triggered the event to prevent race  
>> conditions
>> reading window.location.hash. Other fun things that are a bit more  
>> pie
>> in the sky: the previous hash and knowledge of how it was triggered
>> (manually? pushState? window.location.hash = ? window.location.href =
>> ?).
>
> What are the use cases?

My primary concern here is based upon my experience with having to  
poll to emulate hashchange. In this scenario it is quite possible to  
have two unique actions which are only registered as one (depending  
upon if the hash checking is reactive and responds to a new hash or  
proactive and sets the hash as part of the action and notifies the  
listener to ignore). Thinking about it more you can almost get rid of  
the race condition since hashchange events will always be triggered  
serially by monitoring from the beginning the current value. This  
value would only need to be updated when a hashchange event occurs-- 
but that event must identify the updated value of the hash that  
triggered the hashchange event.

Otherwise there is some possibility of:
1. Navigation request.
2. Script loads, stores initial hash.
3. Hash changes and fires hashchange event 1.
4. Hash changes and fires hashchange event 2. (~12ms apart is about  
the limit)
5. hashchange event 1 is processed, reading location.hash reflects the  
value from event2.

Sure, infinitesimally small chance of triggering it, but it would not  
be fun if it did. As to use cases for more information, knowing how  
the event was triggered would allow for handling it differently (human- 
triggered, need to respond, programmatic, I've already handled it).

>> On Thu, 30 Jul 2009, Nathan Hammond wrote:
>>
>> 2. Clarify that pushState() does not cause navigation.
>>
>> I read the spec quite a few times and still got this wrong,  
>> apparently.
>> Making this completely clear would not hurt.
>
> Could you elaborate on what led you to this conclusion?

Absolutely: my previous "understanding" and misconceptions of how this  
is exactly handled behind the scenes. Maybe it was simply my  
familiarity with having to use the hash that made the other seem  
completely illogical, I've spent many many hours trying to emulate  
history management with browser features that already exist.

When prepping to send comments I read pieces of the spec as they  
applied many times, and things that were only tangentially related I  
didn't necessarily read as carefully. The way it appears to me now, re- 
reading, the spec is written in a way that it only identifies exactly  
what is to be done, and if not explicitly mentioned no other actions  
are taken. Reading the spec in "fill-in-the-blank" mode, which is how  
I did originally, without my new insight into how it appears to be  
written and knowledge of how things work right now had me jumping to  
conclusions about what is supposed to happen.

So I'd say that nothing in particular led me there, I simply wasn't  
steered away. Looking back at my comments it becomes clear that most  
of my concerns it turns out aren't directly related to the spec and  
are more closely things that were simply not completely clear to me.  
And maybe that is an issue we want to address, adding a few statements  
as to what pieces of the spec do not mean would I think go a long way  
in helping those who are interested but a little less immediately  
involved day-to-day (implementers coming to a section for the first  
time).

Best,
Nathan


More information about the whatwg mailing list