Skip to content
This repository

JQM 1.1: Back button fires "pagechange" twice #4094

Closed
zpmaxx opened this Issue · 12 comments

5 participants

zpmaxx Ghislain Seguin Dave Sven Franck ebaranov
zpmaxx

Hi everyone

[Back] button fires "pagechange" event twice in JQM 1.1

Please try 2 demo
Click "List#1" on page "home"->will see one alert on page change
Click "Back" button on "List#1" page->will see 2 alert messages (pagechange will be fired twice)

JQM 1.1 (problem)
http://jsfiddle.net/zpmaxx/TcTwG/

JQM 1.0.1 (everything is ok)
http://jsfiddle.net/zpmaxx/TcTwG/5

With regards, Max
Thnx

Dave

I have noticed this with actual a tag data-role="button" back buttons, and also with direct calls to history.go(-1). It doesn't happen every time either.

Sven Franck

same problem here.

I have been looking into this for a few hours now. I have plastered JQM handleHashChange, changePage and loadPage with console.logs. No difference on correct and double hashchanges.

What I noticed is that the 2nd (unwanted) hashchange triggers a few milliseconds after the first routine has passed, so I was ready to put my money on fake_onhashchange, but that's not it. Looking further.

Sven Franck

@gseguin

I have more or less tracked this down to the pushStateHandler and the onPopState function.

Disable the function call and you have no more trailing hashchange events.

I figure as hashchange fires with URL changes and popState changes the URL after the actual changepage and transition, this could be a reason for an additional hashchange firing when popstate is modifying the URL. I guess it's a timing issue, because sometimes it triggers (popstate too late), sometimes it doesn't (popstate in time). The JQM 1.0. version used a setTimeout here.

Trying to narrow it down.

Sven Franck

@gseguin

Ok. I think I have it:

Problem 1 = fromHash and toHash are always empty strings
This may be my faulty setup, but in the hashValueAfterReset function this line

 $.mobile.path.parseUrl( resetUrl ).hash

always returns empty strings for me. In case the urlHistory has an entry with url like #somePage, I'd expect it to return #somePage and not "". Not sure how to fix this.

Problem 2 = the order inside if (popState) {... } seems wrong.
In the current way it's set up, browser hashchanges can always pass, because:

if( hashChanged ) {
    $win.one( "hashchange.pushstate", function() {
        // -> unblock
        self.nextHashChangePrevented( false );
        });
    }

// -> unblock
self.nextHashChangePrevented( false );

// -> this triggers the binding above = unblock
$.mobile._handleHashChange( poppedState.hash );

if( hashChanged ) {      
    // -> if popstate unblocks after this blocks, browser hashchanges can pass. 
    self.nextHashChangePrevented( true );
    }

To fix this, I re-shuffled like this (and manually set hashChanged to true):

if( hashChanged ) {                         
    // -> if hashChange, set the binding to fire with popstate to unblock
    $win.one( "hashchange.pushstate", function() {
        self.nextHashChangePrevented( false );
        });
    } else {
        // -> otherwise keep false, because no browser hashChange will fire, no need to block          
        self.nextHashChangePrevented( false );
        }

if( hashChanged ) {
    // -> here we lock up to block the browser hashChange
    self.nextHashChangePrevented( true );
    }

// -> here popstate gets triggered, which unlocks, resets the URL, and allows _handlehashChange to run
$.mobile._handleHashChange( poppedState.hash );

it works for me. I only get a single call to _handleHashChange and thus a single changePage only.

I'm not sure whether the browser or popstate induced hashChange should handle the transition. The way I set it up, it will be the popstate that triggers the hashchange. If that's it, I'd be happy to (try my hands on a ) pull request.

Cheers,

Frequent

Ghislain Seguin
Collaborator

@frequent Thanks for the detailed investigation.
I'm picking up your example and we'll see if I reach the same conclusion.

Sven Franck

fingers crossed :-)

Sven Franck

@deAtog

#3347 and #4107

@gseguin - maybe check the above aswell.

Sven Franck

@gseguin

I looked into the first problem, too. Here is a sample page.

This is what's happening, if you have a link like this:

      <a href="#pagetwo">Page two</a>
  • Inside resetUIkeys function JQM is adding the #-hashtag to dialogs and subkeys. Ok.
  • But if a href like above is passed, the function receives pagetwo, doesn't add the hashtag and returns pagetwo
  • In hashValueAfterReset, $.mobile.path.parseUrl( pagetwo ) populates object falsely because of missing #
  • $.mobile.path.parseUrl( pagetwo ).hash will be an empty string passed back as toHash and fromHash
  • Inside onPopState, toHash and fromHash always equal (both empty strings)
  • Thus hashChanged is comparing two empty strings and always returns true(?) - I'm confused as to why...

I have consoled this into the sample page. Goto page 2 > Goto page 3 > go back and check the console.

I guess to solve resetUIkeys would have to add a #-hashtag to links like "pagetwo" as well.

ebaranov

@gseguin

I've sent the pull request pull request, please review it.

Ghislain Seguin
Collaborator
gseguin commented

Fixed by 714266d

Ghislain Seguin gseguin closed this
Sven Franck

Cool! And I was on the right track :-)

Sven Franck

fix without 200ms timeout #4378

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.