Preventing reloading Ajax data when back-button is clicked in both Chrome and IE
Asked Answered
C

2

8

I am currently working on a website in which the home page displays the most recent 10 blog entries. When I scroll down, and when I reach almost the end of the last item on screen, another 10 blog entries are automatically loaded, and so on (this is the infinite scrolling feature).

If a user clicks on any blog entry, then he/she is taken to another page to display the details about that blog entry. When the user clicks the back button, he/she is taken to the home page that has the entries displayed.

Please note, the home page loads the data using Ajax.

Assume the following scenario:

  1. A user goes to the site, and entries 1 to 10 are loaded (via Ajax).

  2. The user scrolls down, and the next 10 entires, specifically entries 11 to 20 are loaded (via Ajax as well). Note that the page now has entires 1 to 20 displayed.

  3. The user scrolls further down, and now entries 21 through 30 are loaded, for a total of 1 through 30 blog entries being displayed on the page.

  4. The user clicks on entry 25, and the page for entry 25 is displayed.

  5. The user clicks the back button, and all items, 1 through 30 are displayed.

Now, if the users uses FireFox, Opera, or Safari, and when that user performs step 5 (i.e, clicks the back button to go back to the home page) then the blog entries are just displayed on screen, and without being re-loaded. However using IE and on Chrome, when the user clicks on the back button, the page is reloaded, and only items 1 through 10 are displayed.

I do not like the IE and Chrome behaviour. The user should see items 1 though 30. How can I ensure all browsers behave like FireFox?

Thanks.

Update

Here is the code I am using

First, hers is my html

<html>
    <body>

        <section id="content">
            <section id="articles">
                <!-- This section is filled by jQuery/Ajax -->
            </section>

            <section id="loading-spinner">
                <img src="ajax-loader.gif" />
            </section>

        </section>

    </body>
</html>

And here is my jQuery

/**
 * 
 * This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
 * ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
 * 
 * .. For more details, please read: https://mcmap.net/q/1187662/-critical-section-in-javascript-or-jquery
 * 
 */


load_more_posts = function () {

    // If we almost reach the bottom of the document, then load more posts
    if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {

        // If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
        if (ajax_status.state() !== "pending") {
            load_posts();

        }
    }
};


function load_posts() {

    ajax_status = jQuery.ajax({

        type:       'post',

        dataType:   'json',

        beforeSend: function(xhr) {

            if(jQuery.data(document.body, 'load_page') == false) {
                xhr.abort();
            }

            else {
                // Show the spinner
                jQuery('#loading-spinner').visible();
            }



        },

        url:        '../link/to/get_poasts.php',

        data:       {
            action:             'load_posts',

            js_query_data:      query_data,
            js_page:        jQuery.data(document.body, 'page_to_load')

        },

        success:    function (response) {

            if (response.isSuccessful) {

                var number_of_post_items = response.posts_array.length;

                for (var i = 0; i < number_of_post_items; i++) {

                    // If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record 
                    if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {

                        // Add 'article'
                        jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');

                        // More code here to add details about each article, such as title, excerpt...etc.


                    }
                }




                // Increase the value of the page to load by 1, and save it.
                page = jQuery.data(document.body, "page_to_load");
                page = page + 1;
                jQuery.data(document.body, "page_to_load", page);



                jQuery(window).on('scroll', load_more_posts);

            }

            else {
                // Display error message
                jQuery('#articles').append('<div>' + response.message + '</div>');

                // Make sure no further AJAX requests are made
                jQuery.data(document.body, 'load_page', false);
            }

        }
    }).always(function() {

        // Hide the spinner
        jQuery('#loading-spinner').invisible();
    });

    return ajax_status;
}



// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)

var ajax_status = new jQuery.Deferred();


jQuery(document).ready(function() {

    // Hide the loading spinner first
    jQuery('#loading-spinner').invisible();


    // We resolve the promise, making sure it is ready (this is an intial state)
    ajax_status.resolve();

    // Initial values that are used
    jQuery.data(document.body, 'page_to_load', 1);

    // This parameter is used to stop loading pages when no more items are available to be displayed 
    jQuery.data(document.body, 'load_page', true);



    // Initial loading of poasts
    load_posts();



    // Enable on scrolling to load more pasts (to allow infinite scrolling)
    jQuery(window).on('scroll', load_more_posts);


});
Caesaria answered 1/8, 2014 at 5:46 Comment(5)
You have a wall of text that ends in "IE and Chrome doing something that other browwers": it is time for you to reduce your problem to a jsfiddle.net or jsbin.com example. Can you reproduce it there? Cool, we can help you. No? Your problem is not where you think it is, and you should first find out where it is before asking us to help you.Libelant
It would depend on how you're doing your page loading. You could try saving all ajax requests to localStorage then when your page loads, you could load all the results (0 to 30) from localStorage instead of doing new ajax calls.Ehrman
@Mike'Pomax'Kamermans : I added the code as you suggested. Hope this helps.Caesaria
@Ehrman : I tried to use sessionStorage instead of localStorage, however I run into a different problem. The problem I run into is when I actually want to refresh the page, it still loads the session data rather than the actual data to be reloaded. The reason for this is those two browsers do not distinquesh between a back button and a page refresh. The both are the same to IE and Chrome unfortunately.Caesaria
Consider using the HTML5 history api. Which should allow you to 'pushState(data)' onto a stack that gets popped when the user hits back. The data you push would be the list items. Override 'onpopstate' on that page to check for the 'data'. Note it is not available in all browser versions: caniuse.com/#search=historyUnthinking
C
1

Just so everyone knows, here is the solution I came up with that is consistent in all browsers. Unfortunately this solution requires a reloead/refresh button to reload the data. I tried to avoid that but could not. Until both IE and Chrome tackle the bfcache issue, I will stick to this solution.

First, here is the new html

<html>
    <body>

        <section id="content">

            <a id="refresh">
                <img src="link/to/refresh.png" title="Refresh" alt="refresh" />
            </a>

            <section id="articles">
                <!-- This section is filled by jQuery/Ajax -->
            </section>

            <section id="loading-spinner">
                <img src="ajax-loader.gif" />
            </section>

        </section>

    </body>
</html>

And this is the javascript

/**
 * 
 * This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
 * ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
 * 
 * .. For more details, please read: https://mcmap.net/q/1187662/-critical-section-in-javascript-or-jquery
 * 
 */


load_more_posts = function () {

    // If we almost reach the bottom of the document, then load more posts
    if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {

        // If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
        if (ajax_status.state() !== "pending") {
            load_posts();

        }
    }
};


function load_posts() {

    ajax_status = jQuery.ajax({

        type:       'post',

        dataType:   'json',

        beforeSend: function(xhr) {

            if(jQuery.data(document.body, 'load_page') == false) {
                xhr.abort();
            }

            else {
                // Show the spinner
                jQuery('#loading-spinner').visible();
            }



        },

        url:        '../link/to/get_poasts.php',

        data:       {
            action:             'load_posts',

            js_query_data:      query_data,
            js_page:            sessionStorage.getItem("page_to_load")

        },

        success:    function (response) {

            if (response.isSuccessful) {

                var number_of_post_items = response.posts_array.length;

                for (var i = 0; i < number_of_post_items; i++) {

                    // If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record 
                    if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {

                        // Add 'article'
                        jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');

                        // More code here to add details about each article, such as title, excerpt...etc.

                        var history_session = get_history_session_name();
                        var history = sessionStorage.getItem(history_session);
                        var article_content = jQuery('#articles').find('#aarticle-' + response.posts_array[i].post_id)[0].outerHTML;

                        sessionStorage.setItem(history_session, history + article_content);

                    }
                }




                // Increase the value of the page to load by 1, and save it.
                page = parseInt(sessionStorage.getItem("page_to_load"));
                page = page + 1;
                sessionStorage.setItem("page_to_load", page);


                jQuery(window).on('scroll', load_more_posts);

            }

            else {
                // Display error message
                jQuery('#articles').append('<div>' + response.message + '</div>');

                // Make sure no further AJAX requests are made
                jQuery.data(document.body, 'load_page', false);
            }

        }
    }).always(function() {

        // Hide the spinner
        jQuery('#loading-spinner').invisible();
    });

    return ajax_status;
}




function get_history_session_name () {
    session_name = 'history___' + escape(location.href);
    return session_name;
}




// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)

var ajax_status = new jQuery.Deferred();


jQuery(document).ready(function() {

    // Hide the loading spinner first
    jQuery('#loading-spinner').invisible();


    // We resolve the promise, making sure it is ready (this is an intial state)
    ajax_status.resolve();

    // This parameter is used to stop loading pages when no more items are available to be displayed 
    jQuery.data(document.body, 'load_page', true);


    // Get the name of the history session
    var history_session = get_history_session_name();

    if (sessionStorage.getItem(history_session) === null) {

        // Set the history session storage
        sessionStorage.setItem(history_session, "");


        // Initial values that are used
        sessionStorage.setItem("page_to_load", 1);

        // Load the posts
        load_posts();

    }


    // Load from history when the back button is clicked
    else {
        jQuery('#articles').append(sessionStorage.getItem(history_session));
    }



    // Enable on scrolling to load more pasts (to allow infinite scrolling)
    jQuery(window).on('scroll', load_more_posts);


    // Reload data when the refresh button is clicked
    // ... We are using a refresh button because if we go to a page that already had history, ...
    // ... and even though we went to that page without using the back button (i.e, via links or directly via the url), ...
    // ... then the history session will be displayed. ...
    // ... Therefore a reload button is needed to overcome this problem if you like to reload data
    jQuery("#refresh").click(function () {

        // Reset/clear the articles section first
        jQuery('#articles').html("");

        // reset the 'load_page' variable
        jQuery.data(document.body, 'load_page', true);


        // Reset/clear the history session storage
        sessionStorage.setItem(history_session, "");

        // Start with loading page 1
        sessionStorage.setItem("page_to_load", 1);

        // Load the posts
        load_posts();
    });

});

Hope this helps.

Caesaria answered 6/8, 2014 at 1:4 Comment(0)
S
1

You'll find some information about how it works in the "good" browsers in this answer:

Is there a cross-browser onload event when clicking the back button?

Here you'll find a way to emulate it in IE:

Differences in Internet Explorer and Firefox when dynamically loading content then going forward and back

Not quite sure why it doesn't work in Chrome though.

Sclerometer answered 1/8, 2014 at 18:25 Comment(0)
C
1

Just so everyone knows, here is the solution I came up with that is consistent in all browsers. Unfortunately this solution requires a reloead/refresh button to reload the data. I tried to avoid that but could not. Until both IE and Chrome tackle the bfcache issue, I will stick to this solution.

First, here is the new html

<html>
    <body>

        <section id="content">

            <a id="refresh">
                <img src="link/to/refresh.png" title="Refresh" alt="refresh" />
            </a>

            <section id="articles">
                <!-- This section is filled by jQuery/Ajax -->
            </section>

            <section id="loading-spinner">
                <img src="ajax-loader.gif" />
            </section>

        </section>

    </body>
</html>

And this is the javascript

/**
 * 
 * This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
 * ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
 * 
 * .. For more details, please read: https://mcmap.net/q/1187662/-critical-section-in-javascript-or-jquery
 * 
 */


load_more_posts = function () {

    // If we almost reach the bottom of the document, then load more posts
    if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {

        // If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
        if (ajax_status.state() !== "pending") {
            load_posts();

        }
    }
};


function load_posts() {

    ajax_status = jQuery.ajax({

        type:       'post',

        dataType:   'json',

        beforeSend: function(xhr) {

            if(jQuery.data(document.body, 'load_page') == false) {
                xhr.abort();
            }

            else {
                // Show the spinner
                jQuery('#loading-spinner').visible();
            }



        },

        url:        '../link/to/get_poasts.php',

        data:       {
            action:             'load_posts',

            js_query_data:      query_data,
            js_page:            sessionStorage.getItem("page_to_load")

        },

        success:    function (response) {

            if (response.isSuccessful) {

                var number_of_post_items = response.posts_array.length;

                for (var i = 0; i < number_of_post_items; i++) {

                    // If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record 
                    if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {

                        // Add 'article'
                        jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');

                        // More code here to add details about each article, such as title, excerpt...etc.

                        var history_session = get_history_session_name();
                        var history = sessionStorage.getItem(history_session);
                        var article_content = jQuery('#articles').find('#aarticle-' + response.posts_array[i].post_id)[0].outerHTML;

                        sessionStorage.setItem(history_session, history + article_content);

                    }
                }




                // Increase the value of the page to load by 1, and save it.
                page = parseInt(sessionStorage.getItem("page_to_load"));
                page = page + 1;
                sessionStorage.setItem("page_to_load", page);


                jQuery(window).on('scroll', load_more_posts);

            }

            else {
                // Display error message
                jQuery('#articles').append('<div>' + response.message + '</div>');

                // Make sure no further AJAX requests are made
                jQuery.data(document.body, 'load_page', false);
            }

        }
    }).always(function() {

        // Hide the spinner
        jQuery('#loading-spinner').invisible();
    });

    return ajax_status;
}




function get_history_session_name () {
    session_name = 'history___' + escape(location.href);
    return session_name;
}




// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)

var ajax_status = new jQuery.Deferred();


jQuery(document).ready(function() {

    // Hide the loading spinner first
    jQuery('#loading-spinner').invisible();


    // We resolve the promise, making sure it is ready (this is an intial state)
    ajax_status.resolve();

    // This parameter is used to stop loading pages when no more items are available to be displayed 
    jQuery.data(document.body, 'load_page', true);


    // Get the name of the history session
    var history_session = get_history_session_name();

    if (sessionStorage.getItem(history_session) === null) {

        // Set the history session storage
        sessionStorage.setItem(history_session, "");


        // Initial values that are used
        sessionStorage.setItem("page_to_load", 1);

        // Load the posts
        load_posts();

    }


    // Load from history when the back button is clicked
    else {
        jQuery('#articles').append(sessionStorage.getItem(history_session));
    }



    // Enable on scrolling to load more pasts (to allow infinite scrolling)
    jQuery(window).on('scroll', load_more_posts);


    // Reload data when the refresh button is clicked
    // ... We are using a refresh button because if we go to a page that already had history, ...
    // ... and even though we went to that page without using the back button (i.e, via links or directly via the url), ...
    // ... then the history session will be displayed. ...
    // ... Therefore a reload button is needed to overcome this problem if you like to reload data
    jQuery("#refresh").click(function () {

        // Reset/clear the articles section first
        jQuery('#articles').html("");

        // reset the 'load_page' variable
        jQuery.data(document.body, 'load_page', true);


        // Reset/clear the history session storage
        sessionStorage.setItem(history_session, "");

        // Start with loading page 1
        sessionStorage.setItem("page_to_load", 1);

        // Load the posts
        load_posts();
    });

});

Hope this helps.

Caesaria answered 6/8, 2014 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.