Skip to content
This repository was archived by the owner on Oct 8, 2021. It is now read-only.

Popup causes navigation back from a redirected page #4595

Closed
DzenisevichK opened this issue Jun 23, 2012 · 15 comments
Closed

Popup causes navigation back from a redirected page #4595

DzenisevichK opened this issue Jun 23, 2012 · 15 comments

Comments

@DzenisevichK
Copy link

Popup (or selectmenu) causes navigation back in history when use $.mobile.changePage(..., { changeHash: false}) in navigation to a page (a redirected page) that contains such widget.

Windows 7 SP1 x64 + Chrome 19.0.1084.56 m:

(from #issues 4563)

@jaspermdegroot
Copy link
Contributor

I adjusted the test page a little to rule out other issues: http://jsbin.com/ivifos/4/

@jaspermdegroot
Copy link
Contributor

Another example of this issue:

http://jquerymobile.com/test/docs/forms/selects/custom.html

Open any of the custom select menus (except the "your state" long list example) and click outside the menu to close it without selecting something. You will be redirected to http://jquerymobile.com/test/docs/forms/selects/

@frequent
Copy link
Contributor

@uGoMobi - I think this is because the custom select triggers a hashchange when (opening/)closing, which is not blocked (ignoreNextHashchange is only "un-set" when the dialog closes... I assume) and therefore the hashchange is passing through $.mobile.changePage landing at the "no to defined" option, which loads the first page.

As a workaround I'm setting flags whenever a popup/custom select or in page dialog is open to preventDefault() hashchanges being triggered from closing elements.

I also asked @gabrielschulhof about adding a role to the data.options passed along with every hashChange event. If there was

data.options.role = 'dialog  || popup || custom-select'

Then it would be easy to check the incoming hashChange event data for the role and block falsely triggered backwards transitions. Maybe the role can replace ignoreNextHashChange altogether as a global setter.

@frequent
Copy link
Contributor

@uGoMobi: thinking about this some more.

Right now popups, custom selects and dialogs all use #&ui-state=dialog as url extension, when semantically only dialogs are state=dialog. Maybe this is another entry point for setting a widget specific flag on triggered events.

@gabrielschulhof
Copy link

@uGoMobi - The custom select double-window.history.back() issue is different from this issue. The latter was a short-lived bug I had accidentally introduced. I really /was/ calling window.history.back() twice instead of just once.

This bug, OTOH, is not specific to popups. Look at http://jsbin.com/iqogin/21 ... Whether you open a popup or a dialog, it goes back to page-0.

@gabrielschulhof
Copy link

@frequent - The #&ui-state=dialog is a kludge, and I'm using it for popups, because it's not easy to introduce another such magic cookie. It's not easy to introduce it because there is special-case-handling code in 1e6 places that deals with this particular magic cookie. I've tried to give popups their own magic cookie - to no avail. There is a serious shortcoming in jqm's navigation system for sure, and it needs a fairly massive fix.

Meanwhile, I'll try some ideas for this particular problem.

@frequent
Copy link
Contributor

frequent commented Jul 4, 2012

@gabrielschulhof - I have popups working more or less with a fix. Once in a while one popup escapes, but most of the times I can catch them.

This is what I'm doing when opening popups (example from a project):

$('#pop_basket')
    .closest('html').addClass('selDiaPopBlock').end()
    .popup('open').find('.popClosure.proceed').on('click.ajax', function(e){
             ajaxFormSubmit( form, service, formdata, targetUrl, successHandler, "no" );
             $('.popClosure.proceed').off('.ajax');
     });

So whenever a popup opens I add selDiaPopBlock to the html element and on pagebeforechange, I'm checking:

  if ( $('html').hasClass('selDiaPopBlock') ){
        // window.setTimeout(function(){
        $('html').removeClass('selDiaPopBlock');
        // },250)
       e.preventDefault();
       return;
       }

Whenever a popup closes I'm cleaning up HTML:

  // close popups on clicking close button 
  $(document).on('click.popup', 'div:jqmData(role="popup"):visible .popClosure', function(e, data) {
        $(this).closest('div:jqmData(role="popup")').popup('close');
        //window.setTimeout(function(){ 
              $('html').removeClass('selDiaPopBlock');
              //    },250);
        });

More or less takes care popups. If I could find a way to detect a custom select or dialog firing (not so easy ...), I would also just the blocker class (after all, it's name selectdialogPopupBlock).

I have fiddled with photoswipe a little and changed their dialog hashkey from popup to gallery and am now using this to block their hashChanges:

 // catch photoswipe with dialog key = #&ui-state=gallery
 if ( typeof parse.hash != "undefined" && parse.hash.replace( /.*#&ui-state=/, "" ) == "gallery" ){                 
      $('html').addClass('ui-swipe-active');
      // a little desperate here...
      e.preventDefault();
      e.stopPropagation();  
      return;
      }

 if ( $('html').hasClass('ui-swipe-active') ) { 
      $('html').removeClass('ui-swipe-active');
      // clean up photoswipe
      $(window).trigger('pageclear');
      e.preventDefault();
      e.stopPropagation();  
      return false;
      }

Maybe usable.

@gabrielschulhof
Copy link

We already have handlers for the case where $.mobile.hashListeningEnabled = false in both popup and dialog, so I think if the page they open from has itself been added with changeHash = false, then they must close via the $.mobile.hashListeningEnabled = false code path rather than window.history.back().

@gabrielschulhof
Copy link

The fundamental issue is that, given the navigational sequence below,

urlHistory:
[ initial page ]
-> [ page loaded normally ] -> ... -> [ # page loaded normally # ]
-> [ page loaded without hash change ] -> ... -> [ | page loaded without hash change | ] -> *[ dialog ]*

... where *[]* indicates the page currently being viewed and where "page loaded without hash change" means "page loaded with $.mobile.changePage( url, { changeHash: false } )"

when the dialog issues window.history.back() upon close, the hash given to _handleHashChange will be that of the last page loaded normally (i.e., with hash change - enclosed in [ # # ] in the diagram above). This will cause that page to be displayed.

This is incorrect. The page that should be displayed is, of course, the last page that was loaded without hash change (enclosed in [ | | ] in the diagram above).

To achieve this, we need to

  1. Record the value of changeHash in each urlHistory entry

and

2 a. Create a navigation policy such that changeHash: false entries are to some extent "children" of the last known changeHash: true entry, and it is the last child that will be switched to upon window.history.back()

xor

2 b. Modify the dialog's and the popup's close method to behave similarly to the $.mobile.hashListeningEnabled = false case in that they do not call window.history.back(), but instead they call some new function within the navigation that performs the jqm equivalent of window.history.back() without an actual hashchange.

I'm leaning towards 2 b.

It'd be nice to get the opinion of one @johnbender on this issue :)

@frequent
Copy link
Contributor

frequent commented Jul 5, 2012

@gabrielschulhof: Is the _handleHashChange behavior you are talking about the "no to defined" option?

This is one piece of "hashChange" I would like to get rid of if I could :-)

@gabrielschulhof
Copy link

What I'm talking about is this:

Since you've added the page with changeHash: false, when the dialog goes window.history.back(), _handleHashChange will get the hash of page-0, because page-1 did not have a hash since you added it with changeHash: false. So, the nav dutifully goes back to page-0.

Currently, I can't find a good place where to add a special case saying "if you're coming back from a dialog then go to the previous page in urlHistory, not to the page identified by the hash given to you in _handleHashChange".

@frequent
Copy link
Contributor

frequent commented Jul 5, 2012

How about querying the urlHistory for role?

(Without looking into any code), when I'm opening a dialog I create an entry in the urlHistory, which also saves settings role. So when closing the dialog and going back, one would have to check for the role of the last urlHistory entry. If it has a role="dialog" we should be tryring to go back from the dialog and you could tap in and set the backwards transition accordingly.

Makes sense?

@gabrielschulhof
Copy link

We've agreed that we cannot currently add a special case for going back to a page that was added with changeHash: false.

@christhorsvik
Copy link

I found an easy solution. After traveling to a page through a "changeHash=false" link, simply replace the last history item.

var targetId = $(e.target).attr('href');
$(':mobile-pagecontainer').pagecontainer('change', targetId, {changeHash:false});
window.history.replaceState(undefined, undefined, targetId);

I hope this helps someone.

@GopiKrishna06
Copy link

Any updates on this issue i have a similar problem. One more extra problem for me is that when a option is selected from drop down and enter key is pressed simultaneously it acts like a browser history back.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants