How to make the browser "Back" button work on an AJAX site?

Asked

Viewed 6,633 times

15

I researched this topic a year ago when I made a site with features like $.load() and $.ajax() in AJAX/jQuery but did not get anything satisfactory that was not overly complex, and until today the site goes back to the homepage when the user clicks on the button Voltar browser.

What would be a simple method to restore the previous state before the browser receives the new data via AJAX?

What is the best method to "print" on the browser URL a change to the target AJAX is searching for? The same way it happens when you click an image on Facebook and it changes the URL even having obtained the data via AJAX.

I would like to use the URL in the same style as I do the ajax calls: dominio.com/aplicativo/controlador/ação/parâmetros instead of using #.

  • I spent two hours preparing an answer, but now your question indicates a detail that makes it impossible in a way!!! I will leave it anyway because it deals with most of the problems presented. It just does not solve the question of using something other than the hash to control the URL.

4 answers

12


Introducing

In order for the "Back" and "Forward" buttons that browsers provide to be used on a web-site whose contents are loaded asynchronously, it is necessary to manipulate the browser’s own browsing history.

The way in which this type of manipulation can actually be carried out without an update of the page (refresh), is through the use of hash locational.

Whenever we hash the current browser location, it will create a new entry in the browsing history. This will then allow the visitor to navigate a site whose contents are loaded asynchronously, through the use of the navigation arrows of the browser itself, without actually updating the page.


Needs

To enable the manipulation of hash of the current location in the browser, there are three basic requirements:

  • Read: Collect and process the value of hash at current location;
  • Write: Update to hash at the current location with the desired value;
  • Listen: Detect when an amendment was made to hash and act accordingly.

Let’s see how to deal with each of these requirements:

  • Read

    Collect the hash of current location

    To read the hash of the current location, we can use Javascript and read the value of the property window.location.hash. The first character will always be the indicator of hash #, that can be disposed of.

    // ler valor e "cortar" o primeiro caractere
    var hashValue = window.location.hash.substring(1);
    

    For the sake of efficiency, let us create a function to deal with this action:

    /**
     * Devolve a hash da localização actual
     * @return {string} Valor da Hash com prefixo '#' ignorado.
     */
    function lerHash () {
      return window.location.hash.substring(1);
    }
    
  • Write down

    Write a value on hash of current location

    To update the hash of the current browser location programmatically, we again resort to Javascript and the property window.location.hash:

    // atribuir valor à hash
    window.location.hash = 'pagina2';
    

    Once again, for reasons of efficiency, let us create a function to deal with this action:

    /**
     * Actualiza a hash da localização actual com o valor facultado
     * @param {string} str
     */
    function escreverHash(str) {
      window.location.hash = str;
    }
    
  • Listen to

    Listen to and detect a change in hash current browser path

    The browser conveniently triggers an event whenever the hash the current location has been updated or changed. To be aware of this update and trigger an action using the new value assigned, we just need to configure an event handler using Javascript to listen to this event:

    /**
     * Escuta alterações na hash da localização actual.
     * @param {Event} e HashChangeEvent object
     */
    window.onhashchange = function(e) {
      // Fazer algo quando existe uma alteração de valor
      // ...
    };
    

Use of hash to allow a "persistent" status of the web page

To understand a little how everything is working behind the scenes, let’s look at the following scheme:

Sequência de Acções

  • Action sequence A: Color Red

    Here is illustrated the normal behavior of the browser, without the implementation of the requirements we have seen above:

    • User interacts with page;
    • The page updates the hash to open a new entry in browsing history;
    • The update of hash triggers an event that in turn will call the manipulative function;
    • Manipulative function does nothing.
  • Action sequence B: Color Green

    Here is illustrated what happens with the application of the requirements we study above:

    • User updates the address in the browser or use the "Back/Forward" buttons of the browser itself;
    • The update of hash triggers an event that in turn will call the manipulative function;
    • Manipulator function will perform a function our to update the page;
    • Page is updated and nothing else happens (none refresh).

Practical example

Since in your question you have no code to better illustrate your practical scenario of adapting any answer to your specific case, here is a generic example.

In this example the web page simulates the insertion of contents via Ajax and manipulates the elements to be presented to the user, while trying to create new entries in the history of browser locations to allow the use of the "Back" or "Forward" buttons without causing an update (refresh):

<!doctype html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Exemplo de navegação sem actualização da página</title>
    </head>
    <body>
      <nav>
        <ul>
          <li id="pagina1">
            Página 01
          </li>
          <li id="pagina2">
            Página 02
          </li>
          <li id="pagina3">
            Página 03
          </li>
          <li id="pagina4">
            Página 04
          </li>
        </ul>
      </nav>
      <section>
        Olá, bem vindo à Página 01!
      </section>

      <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>

      <script type="text/javascript">

        function cumprimenta(pagina){

          $('section').delay(100).html( "Olá, bem vindo à " + $('#'+pagina).html() );

          $('nav li').css("color", "black");
          $this.css("color", "green");
        }

        function oraBolas() {
          $('section').delay(100).html( "Existe qualquer coisa de errado!" );
        }

        /**
         * Escuta alterações na hash da localização actual.
         * @param {Event} e HashChangeEvent object
         */
        window.onhashchange = function(e) {

          var pagina = lerHash();

          // aqui chama-se função X mediante o valor da variável "pagina"

          if (pagina.length==7)
            cumprimenta(pagina);
          else
            oraBolas();
        };

       /**
        * Actualiza a hash da localização actual com o valor facultado
        * @param {string} str
        */
       function escreverHash(str) {
         window.location.hash = str;
       }

       /**
        * Devolve a hash da localização actual
        * @return {string} Valor da Hash com prefixo '#' ignorado.
        */
       function lerHash () {
         return window.location.hash.substring(1);
       }

       $(function() {

         $('nav').on("click", 'li', function (e) {
           e.preventDefault();
           escreverHash($(this).attr("id"));
         });
       });
     </script>
   </body>
</html>

Completion

In this way, manipulating the hash, a consistent navigation throughout the web-site is achieved, either by the use of the links present in the page currently being presented, or by the use of the arrows "Back" and "Forward" of the browser itself.

The work is all performed by the listening event of the hash, where the relevant function which will give rise to the intended behaviour or changes is to be called here.

I think you can solve both problems in the question like this:

  • Direct the visitor to a certain area without refresh;
  • Ensure that, if the visitor uses the navigation arrows present in the browser itself, the web-site will present you the page previous or next as expected once again without refresh.

5

Although I don’t know enough to delve into the subject, there is this library history js. which provides several methods to manipulate browser history that works with both Html5 and html4.

Code taken from library documentation:

(function(window,undefined){

    // Bind para o evento StateChange 
    History.Adapter.bind(window,'statechange',function(){ // Nota: Estamos usando statechange em vez de popstate
        var State = History.getState(); // Nota: Estamos usando History.getState() em vez event.state
    });

    // Modificando os Estados
    History.pushState({state:1}, "State 1", "?state=1"); // cria {state:1}, "State 1", "?state=1"
    History.pushState({state:2}, "State 2", "?state=2"); // cria {state:2}, "State 2", "?state=2"
    History.replaceState({state:3}, "State 3", "?state=3"); // cria {state:3}, "State 3", "?state=3"
    History.pushState(null, null, "?state=4"); // cria {}, '', "?state=4"
    History.back(); // acessa {state:3}, "State 3", "?state=3"
    History.back(); // acessa {state:1}, "State 1", "?state=1"
    History.back(); // acessa {}, "Home Page", "?"
    History.go(2); // acessa {state:3}, "State 3", "?state=3"

})(window);

The idea here would be to create a pushState for every AJAX call you make. So when the user clicks the button Come back it will return to the previously created state.

Still, it is perfectly possible to use urls as you mentioned:

History.pushState({state: 1}, "Editar 1", "editar/1"); 

And the browser will display in the navigation bar http://seudominio.com/controller/editar/1

  • when using back() he will remake the DOM of the page to remove changes made by the last AJAX?

  • Yes, when the site user clicks on the Back of the browser it will go back to the state you created. See that with History.back(); you are using the object of this library and not directly the history.back(); browser. Take a look at the link have some examples there...

  • +1 history.js is a good move; ...but I don’t know if it’s compatible with Angularjs...?

  • I have not tested with Angularjs, in the version list there is no one specific for Angular, but there is one that is cited as native which is Native.history.js

4

A direct solution is the HTML5 History API:

history.pushState( estado, titulo, url );

To make more complete code and a better user experience, you can implement the popstate when changing the content, so that the Backspace key or the "back" button of the browser works correctly, returning to the previous situation, according to its implementation:

window.addEventListener( "popstate", function(e) {
   // Aqui vai o código para voltar ao estado anterior da navegacao
});

The first parameter of the pushState mentioned above (state), serves precisely to save the data you want, to be able to use in the function above. If you prefer, you can process the URL directly, and not use the/state.

Here’s a nice tutorial on this link: http://diveintohtml5.info/history.html

Here, a demo of a photo gallery in ajax, with history, and its respective source.

Note that in older browsers, Urls work perfectly, even without Ajax.

3

For this you need to authenticate the URL so that the browser really comes back. In the case of AJAX I know two ways, one is with URL comments, the famous hash (#) or another method that I find more organized although you would have to treat the URL’s also by . htaccess, in this case you would use History API, more specifically history.pushState which is a method of changing the URL without redirecting the browser.

The first case is simple:

Location.hash = '!/test/lorem-ipsum'

Note: I used "!" to index dynamic content, google replaces "#!" with "? _escaped_fragment_=" to index dynamic content. You can read more about this at: https://developers.google.com/webmasters/ajax-crawling/docs/specification

A simple example of the second case would be:

history.pushState(null, null, 'teste/lorem-ipsum');

This script would already change the URL and you can use API events to run your AJAX when the URL changes, in this case the event window.onpopstate.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.