Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

It should be possible to disable history #5465

Closed
jhogervorst opened this Issue · 14 comments

4 participants

@jhogervorst

The current history implementation of jQuery Mobile is great for websites. Users can navigate between pages, bookmark pages and share pages via email or social media. It's an impressive technique and it seems to work great … for websites.

I'm currently building a web application using jQuery Mobile. The difference here is that I'm using iOS concepts as much as possible, including its navigation system. In iOS there's no back and forward button; you only have the buttons that are available in the current screen.

The real problem is that the current history system gives much control to the users. Control that I (as developer) lose. How can I guarantee that the application will work correctly, when users can navigate back to pages that might be changed or deleted, for example? These problems do not exist in iOS because of the different navigation system.

I hope you will consider making it possible to disable all history features and give full control to the developer. I understand that this is not necessary for most websites, but for native-like applications it's a must.

@gabrielschulhof
Collaborator

Before you include jQuery Mobile, add a script like this:

<script>
$( document ).bind( "mobileinit", function() {
  $.mobile.hashListeningEnabled = false;
  $.mobile.pushStateEnabled = false;
  $.mobile.changePage.defaults.changeHash = false;
});
</script>

This should turn off history manipulation.

Consult the docs for other useful settings you can modify during the "mobileinit" handler.

If this still doesn't sufficiently cover your needs, please reopen this issue.

@jhogervorst

Thanks for your quick reply.

It works great … until you try to close a dialog. When pressing close button, the dialog animates away and you end up with an empty screen. I assume dialogs use the history to determine which page to open when they're closed.

For now used the following workaround:

$.mobile.pushStateEnabled = false;
$.mobile.hashListeningEnabled = false;

// Function copied from jquery.mobile-1.3.0-beta.1.js #2066-2132
// Modifications are marked with "[MODIFICATION]"
$.mobile.Navigator.prototype.go = function( url, data, noEvents ) {
    // [MODIFICATION: Line copied from #1994]
    var path = $.mobile.path;
    // [/MODIFICATION]

    var state, href, hash, popstateEvent,
        isPopStateEvent = $.event.special.navigate.isPushStateEnabled();

    // Get the url as it would look squashed on to the current resolution url
    href = path.squash( url );

    // sort out what the hash sould be from the url
    hash = this.hash( url, href );

    // Here we prevent the next hash change or popstate event from doing any
    // history management. In the case of hashchange we don't swallow it
    // if there will be no hashchange fired (since that won't reset the value)
    // and will swallow the following hashchange
    if( noEvents && hash !== path.stripHash(path.parseLocation().hash) ) {
        this.preventNextHashChange = noEvents;
    }

    // IMPORTANT in the case where popstate is supported the event will be triggered
    //      directly, stopping further execution - ie, interupting the flow of this
    //      method call to fire bindings at this expression. Below the navigate method
    //      there is a binding to catch this event and stop its propagation.
    //
    //      We then trigger a new popstate event on the window with a null state
    //      so that the navigate events can conclude their work properly
    //
    // if the url is a path we want to preserve the query params that are available on
    // the current url.
    this.preventHashAssignPopState = true;

    // [MODIFICATION: Added comment to disable line]
    //window.location.hash = hash;
    // [/MODIFICATION]

    // If popstate is enabled and the browser triggers `popstate` events when the hash
    // is set (this often happens immediately in browsers like Chrome), then the
    // this flag will be set to false already. If it's a browser that does not trigger
    // a `popstate` on hash assignement or `replaceState` then we need avoid the branch
    // that swallows the event created by the popstate generated by the hash assignment
    // At the time of this writing this happens with Opera 12 and some version of IE
    this.preventHashAssignPopState = false;

    state = $.extend({
        url: href,
        hash: hash,
        title: document.title
    }, data);

    if( isPopStateEvent ) {
        popstateEvent = new $.Event( "popstate" );
        popstateEvent.originalEvent = {
            type: "popstate",
            state: null
        };

        this.squash( url, state );

        // Trigger a new faux popstate event to replace the one that we
        // caught that was triggered by the hash setting above.
        if( !noEvents ) {
            this.ignorePopState = true;
            $.mobile.window.trigger( popstateEvent );
        }
    }

    // record the history entry so that the information can be included
    // in hashchange event driven navigate events in a similar fashion to
    // the state that's provided by popstate
    this.history.add( state.url, state );
};

It shouldn't be too hard to introduce a new setting which controls whether the window.location.hash line is executed, right?

By the way, I can't find a way to reopen this issue, but please consider it as reopened ;-)

@johnbender johnbender was assigned
@gabrielschulhof
Collaborator

@johnbender, window.location.hash should not be set if $.mobile.hashListeningEnabled is turned off, or, at any rate, we should have a unified way of turning off history. $.mobile.hashListeningEnabled used to be the key switch for turning off history.

Also $.mobile.changePage.defaults.changeHash no longer has any effect, because changePage no longer sets the hash itself.

@gabrielschulhof
Collaborator

Hold on a sec - I can't reproduce this going-to-blank-page thing ... I've tried this. @jhogervorst, is this what you're trying to do? In the above example, if you

  1. click "Open Dialog"
  2. click the resulting dialog's "Close" button

you end up back on the page that opens the dialog, not on a blank page.

Granted, that is with latest master, but if that works for you then that means this issue is already resolved.

@gabrielschulhof
Collaborator

I've since modified the above-mentioned test to use 1.3.0 beta, and it also works: http://jsbin.com/amozef/106

@jhogervorst

I'm going to take a look right now.

@jhogervorst

Got it: when closing a dialog with history disabled, you return to the page that's in the source of the original HTML file.

Here's a two-page example. Page 1 is included in the source. Page 2 is loaded via AJAX from page 1. The dialog is loaded via AJAX from page 2. When you close the dialog, you'll return to page 1 (the page included in the source).

Here's a different example, where no page is included in the source. Page 1 is loaded via AJAX during page load. Page 2 is loaded via AJAX from page 1. The dialog is loaded via AJAX from page 2. When you close the dialog, you'll get a blank screen.

@gabrielschulhof
Collaborator

@jhogervorst

I couldn't get the second example to work. When I follow your second link, I get a blank page, so it doesn't look like page 1 is getting loaded.

Nevertheless your first link illustrates the problem well enough. Closing the dialog should not cause a jump back to page 1, but to page 2.

@gabrielschulhof
Collaborator

@johnbender

The dialog's close() method uses $.mobile.urlHistory.getActive() to determine which page to go back to. However, I have observed that, with the above three flags set, no urlHistory is being created. Thus, the dialog picks the first and only history entry and goes there.

@gabrielschulhof
Collaborator

Furthermore, this is happening because $.mobile.changePage doesn't call $.mobile.navigate if changeHash is off. However, I think we should have a way of adding to urlHistory even without modifying the hash for cases like this. I've tried this, but it's not as simple as that.

@johnbender

@jhogervorst

I'm guessing I introduced this bug when I re-implemented $.mobile.hashListeningEnabled. Part of the problem is that it's not clear what that setting accomplished before the refactor.

That is, we didn't have any tests for it :(

@gabrielschulhof

When $.mobile.hashListeningEnabled is false how are dialogs supposed to work? Recall that previously our entire navigation infrastructure was based on hash events so for the most part this means that navigation is "OFF". At the very least that was the assumption I made when re-implementing it.

More importantly when I made the addition I did my testing with the assumption that $.mobile.linkBindingEnabled was also false (stupid). So it might be that we only need to figure out what it means to have link binding enabled but to have hash listening disabled. This is really just a different way of wording your last comment, ie "we should have a way of adding to urlHistory even without modifying the hash for cases like this".

@jhogervorst

@johnbender Thanks for the extensive reply. I don't know very much about the internal working of jQuery Mobile, but in my experience it turned out to be possible to disable changes of the URL/hash by just commenting out the line window.location.hash = hash; in $.mobile.Navigator.prototype.go() (see code example from four days ago). Wouldn't that be an easy solution (or is it too easy)?

@johnbender

@jhogervorst

It depends. Calling $.mobile.navigate comes with some expectations, including history entry creation that matches the browsers history. Breaking that assumption might hurt other parts of the library.

Also we want to keep those settings confined to the old nav code where we can. They're already bleeding into the navigate event. The goal is to have these stand on their own as functionality for users who don't want to use the whole of JQM.

@gabrielschulhof
Collaborator

@johnbender

$.mobile.hashListeningEnabled = false;
$.mobile.changePage.defaults.changeHash = false;

These two settings in concert pretty much turn off browser history handling. Dialog and popup both have provisions for dealing with the situation where modifications of the hash are undesirable. Navigation is then based solely on $.mobile.urlHistory and $.mobile.changePage, which itself had code for avoiding the call to $.mobile.path.set(), which would then set the hash. However, with $.mobile.navigate() the setting of the hash has been tied to the creation of urlHistory entries, which makes the above logic impossible.

@jefflembeck jefflembeck was assigned
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.