Fix back button issue on load more pagination style - javascript

HTML
<div class="moreButton">
<a class="more" id="<?php echo htmlspecialchars($page);?>">More</a>
</div>
AJAX
$(function(){
$('.more').live('click', function(){
var page = $(this).attr('id'); //get the last id
$.ajax({
type : 'GET',
url : 'functionality/js/paginate.php',
data : { page : page, per_page : per_page, last_page : last_page },
beforeSend: function(){
$('.more').html(img);
if(history.pushState){
history.pushState(null, null, '#' + page);
}else{
location.hash = '#' + page;
}
},
success: function(data){
$('.more').remove();
$('.main-content').append(data);
}
});
});
});
I've implemented a load_more style of pagination. The problem here is the usual for infinite scrolls, when a user clicks a post and comes back with back button, he/she should get the previous number of loaded posts, but only initial posts are loaded. I'm trying to integrate the history.pushState functionality based on what I found googling, but doesn't seem to get it working. What am I missing here?

There are two key parts to saving states using browser history. The pushState function allows you to add to the history stack (essentially like going to a new page). It also allows you to store a javascript object as the "state". This will come in handy when the state is "popped" off the stack (e.g. the browser's "back" button is pressed).
Browsers throw a popstate event which you can use to determine if the browser is going back to a previous state. You can access it with window.onpopstate. To watch for a hash change you can use window.onhashchange.
if ("onpopstate" in window) {
window.onpopstate = function (event) {
if (event.state && event.state.pageID) {
fetchData(event.state.pageID);
}
};
}
if ("onhashchange" in window) {
window.onhashchange = function () {
if (location.hash) {
fetchData(location.hash.substr(1));
}
};
}
function fetchData(pageID) {
// Load some content
}
function saveState(pageID) {
if (history.pushState) {
history.pushState({ pageID: pageID }, null, "/page/" + pageID);
} else {
location.hash = pageID;
}
}

Here you need to define a function to check hash update as if hash updates(User clicks on back/forward button) it should update data of the page according to URL.

Related

Forward button not working after history.pushState

I've found how to fix the back button, but the forward button has remained unfix-able. The url will change but the page doesn't reload, this is what I'm using:
$('.anchor .wrapper').css({
'position': 'relative'
});
$('.slanted').css({
'top': 0
});
// Do something after 1 second
$('.original-page').html('');
var href = '/mission.html'
console.log(href);
// $('#content-div').html('');
$('#content-div').load(href + ' #content-div');
$('html').scrollTop(0);
// loads content into a div with the ID content_div
// HISTORY.PUSHSTATE
history.pushState('', 'New URL: ' + href, href);
window.onpopstate = function(event) {
location.reload();
};
// response.headers['Vary'] = 'Accept';
// window.onpopstate = function (event) {
// alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
// location.reload();
// response.headers['Vary'] = 'Accept';
// };
// $(window).bind('popstate', function () {
// window.onpopstate = function (event) {
// window.location.href = window.location.href;
// location.reload();
// };
e.preventDefault();
As you can see, I've tried several different things, and the back button works just fine but not the forward button.
Keep in mind that history.pushState() sets a new state as the newest history state. And window.onpopstate is called when navigating (backward/forward) between states that you have set.
So do not pushState when the window.onpopstate is called, as this will set the new state as the last state and then there is nothing to go forward to.
Complete solution working with multiple clicks on back and forward navigation.
Register globally window.onpopstate event handler, as this gets reset on page reload (and then second and multiple navigation clicks don't work):
window.onpopstate = function() {
location.reload();
};
And, update function performing AJAX reload (and, for my use-case replacing query parameters):
function update() {
var currentURL = window.location.href;
var startURL = currentURL.split("?")[0];
var formParams = form.serialize();
var newURL = startURL + "?" + formParams;
var ajaxURL = startURL + "?ajax-update=1&" + formParams;
$.ajax({
url: ajaxURL,
data: {id: $(this).attr('id')},
type: 'GET',
success: function (dataRaw) {
var data = $(dataRaw);
// replace specific content(s) ....
window.history.pushState({path:newURL},'',newURL);
}
});
}
I suggest reading about navigating browser history and the pushState() method here. It explicitly notes that pushState() by itself will not cause the browser to load a page.
As far as the forward button not working, once you call pushState() the browser is (conceptually) at the last (latest) page of the history, so there is no further page to go "forward" to.

Popstate event not triggered after pushing twice to history using history.pushState()

I’m working on an eshop where items are opened on top of a page in iframes. I’m using
history.pushState(stateObj, "page 2", http://localhost:8888/product-category/tyger/vara-tyger/?view=product&item=test-4);
in order to let customers copy the current url and use it to go to the current page with the item opened in an iframe. In addition, I’m using
window.addEventListener('popstate', manageHistory);
function manageHistory(event) {
if (!has_gone_back) {
var iframeOpen = false;
has_gone_back = true;
}
else {
var iframeOpen = true;
has_gone_back = false;
}
}
in order to let customers use their browser’s back and forward buttons for navigation (closing and opening the iframe).
However, when opening one product (calling history.pushState once), using the browser’s back button, and opening another product (calling history.pushState again), and going back again, manageHistory() is not called. The customer is taken to the first opened product but if pressing back again, manageHistory() is called.
I want manageHistory() to be called when pressing back on the product page opened second in order to add code to redirect customers to the category's start page when pressing back.
I’ve tried both adding Event Listeners for both opened products and also for only the first one. Any ideas what the problem may be?
From https://developer.mozilla.org/en-US/docs/Web/Events/popstate
Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by doing a browser action such as a click on the back button (or calling history.back() in JavaScript).
You can overwrite popState and replaceState, but what is generally a better idea is to create a wrapper which sets the url and then triggers your handler function.
Something like this...
function urlChangeHandler() {
var url = window.location.href;
// Whatever you want to do...
}
// Handle initial url:
urlChangeHandler();
window.addEventListener('popstate', urlChangeHandler);
var urlState = {
push: function(url) {
window.history.pushState(null, null, url);
urlChangeHandler();
},
replace: function(url) {
window.history.replaceState(null, null, url);
urlChangeHandler();
}
}
I have a similar file in one of my projects which updates the datastore based on the #hash...
import tree from './state'
// No need for react-router for such a simple application.
function hashChangeHandler(commit) {
return () => {
const hash = window.location.hash.substr(1);
const cursor = tree.select('activeContactIndex');
const createCursor = tree.select('createNewContact');
cursor.set(null);
createCursor.set(false);
(() => {
if(!hash.length) {
// Clean up the url (remove the hash if there is nothing after it):
window.history.replaceState(null, null, window.location.pathname);
return;
}
if(hash === 'new') {
createCursor.set(true);
return;
}
const index = parseInt(hash, 10);
if(!isNaN(index)) {
cursor.set(index);
}
})();
commit && tree.commit();
}
}
// Handle initial url:
hashChangeHandler(true)();
// Handle manual changes of the hash in the url:
window.addEventListener('hashchange', hashChangeHandler(true));
function createHash(location) {
return (location !== null) ? `#${location}` : window.location.pathname;
}
module.exports = {
push: (location, commit=true) => {
window.history.pushState(null, null, createHash(location));
hashChangeHandler(commit)();
},
replace: (location, commit=true) => {
window.history.replaceState(null, null, createHash(location));
hashChangeHandler(commit)();
}
}

Back button / backspace does not work with window.history.pushState

I have made a solution for my website which includes using ajax to present the general information on the website. In doing this, I am changing the URL every time a user loads some specific content with the window.history.pushState method. However, when I press backspace or press back, the content of the old url is not loaded (however the URL is loaded).
I have tried several solutions presented on SO without any luck.
Here is an example of one of the ajax functions:
$(document).ready(function(){
$(document).on("click",".priceDeckLink",function(){
$("#hideGraphStuff").hide();
$("#giantWrapper").show();
$("#loadDeck").fadeIn("fast");
var name = $(this).text();
$.post("pages/getPriceDeckData.php",{data : name},function(data){
var $response=$(data);
var name = $response.filter('#titleDeck').text();
var data = data.split("%%%%%%%");
$("#deckInfo").html(data[0]);
$("#textContainer").html(data[1]);
$("#realTitleDeck").html(name);
$("#loadDeck").hide();
$("#hideGraphStuff").fadeIn("fast");
loadGraph();
window.history.pushState("Price Deck", "Price Deck", "?p=priceDeck&dN="+ name);
});
});
Hope you guys can help :)
pushState alone will not make your page function with back/forward. What you'd need to do is listen to onpopstate and load the contents yourself similar to what would happen on click.
var load = function (name, skipPushState) {
$("#hideGraphStuff").hide();
// pre-load, etc ...
$.post("pages/getPriceDeckData.php",{data : name}, function(data){
// on-load, etc ...
// we don't want to push the state on popstate (e.g. 'Back'), so `skipPushState`
// can be passed to prevent it
if (!skipPushState) {
// build a state for this name
var state = {name: name, page: 'Price Deck'};
window.history.pushState(state, "Price Deck", "?p=priceDeck&dN="+ name);
}
});
}
$(document).on("click", ".priceDeckLink", function() {
var name = $(this).text();
load(name);
});
$(window).on("popstate", function () {
// if the state is the page you expect, pull the name and load it.
if (history.state && "Price Deck" === history.state.page) {
load(history.state.name, true);
}
});
Note that history.state is a somewhat less supported part of the history API. If you wanted to support all pushState browsers you'd have to have another way to pull the current state on popstate, probably by parsing the URL.
It would be trivial and probably a good idea here to cache the results of the priceCheck for the name as well and pull them from the cache on back/forward instead of making more php requests.
This works for me. Very simple.
$(window).bind("popstate", function() {
window.location = location.href
});
Have same issue and the solution not working for neither
const [loadBackBtn, setLoadBackBtn] = useState(false);
useEffect(() => {
if (loadBackBtn) {
setLoadBackBtn(false);
return;
} else {
const stateQuery = router.query;
const { asPath } = router;
window.history.pushState(stateQuery, "", asPath);
},[router.query?.page]

getting my back and forwards buttons to work with ajax

My site is working much quicker thanks to some code I painstakingly modified, but I would love if the browsers' back/forwards buttons worked. Right now, with my code below, the browser address bar never changes. When someone clicks 'Back', it takes them out of the application.
Would there by any easy way of changing this so the browser's back/forward button worked? Or else if someone could point me in the right direction. Thanks for any help.
$(document).on("ready", function () {
//I want to load content into the '.page-content' class, with ajax
var ajax_loaded = (function (response) {
$(".page-content")
.html($(response).filter(".page-content"));
$(".page-content .ajax").on("click", ajax_load);
});
//the function below is called by links that are described
//with the class 'ajax', or are in the div 'menu'
var history = [];
var ajax_load = (function (e) {
e.preventDefault();
history.push(this);
var url = $(this).attr("href");
var method = $(this).attr("data-method");
$.ajax({
url: url,
type: method,
success: ajax_loaded
});
});
//monitor for clicks from menu div, or with
//the ajax class
//why the trigger?
$("#menu a").on("click", ajax_load);
$(".ajax").on("click", ajax_load);
$("#menu a.main").trigger("click");
});
Here is a way of detecting what you are asking.
Bear in my playing with the back and forward buttons is a risky task.
window.onload = function () {
if (typeof history.pushState === "function") {
history.pushState("someState", null, null);
window.onpopstate = function () {
history.pushState("newState", null, null);
// Handle the back (or forward) buttons here
// Will not handle refresh, use onbeforeunload for this.
};
}
}
You could use the jquery address plugin (http://www.asual.com/jquery/address/).
It has an event that detects when the user presses the back/forward button.
$.address.externalChange(function() { console.log('back/forward pressed'); });
As far as I know there is no way of differentiating between back and forward.
You should definitely check History.js
Here's some sample code:-
(function(window,undefined){
// Prepare
var History = window.History; // Note: We are using a capital H instead of a lower h
if ( !History.enabled ) {
// History.js is disabled for this browser.
// This is because we can optionally choose to support HTML4 browsers or not.
return false;
}
// Bind to StateChange Event
History.Adapter.bind(window,'statechange',function(){ // Note: We are using statechange instead of popstate
var State = History.getState(); // Note: We are using History.getState() instead of event.state
//History.log(State.data, State.title, State.url);
var goto_url = State.url;
$.ajax({
url: goto_url,
dataType: "script"
});
});
})(window);

How to use jQuery click event to change href value asynchronously, based on a JSON query

I'm using the bit.ly url shortening service to shorten certain url's being sent to a "share on twitter" function. I'd like to load the bit.ly url only when a user actually presses the share button (due to bit.ly's max 5 parallel reqs limitation). Bit.ly's REST API returns a JSON callback with the shortened url, which makes the whole scenario async.
I've tried the following to stop the click event, and wait for the JSON call to return a value before launching the click.
I have the following simplified code in jQuery(document).ready():
Updated code (oversimplified)!
jQuery("#idofaelement").click(function(event) {
event.preventDefault(); //stop the click action
var link = jQuery(this);
bitlyJSON(function(shortUrl) {
link.attr("href", function() {
//process shortUrl
//return finalized url;
}).unbind().click();
});
});
And the following code to handle the bitly shortening (works just fine):
function bitlyJSON(func) {
//
// build apiUrl here
//
jQuery.getJSON(apiUrl, function(data) {
jQuery.each(data, function(i, entry) {
if (i == "errorCode") {
if (entry != "0") {
func(longUrl);}
} else if (i == "results") {
func(entry[longUrl].shortUrl);}
});
});
} (jQuery)
The href gets its value changed, but the final .click() event never gets fired. This works fine when defining a static value for the href, but not when using the async JSON method.
As you outlined yourself:
event.preventDefault(); //stop the click action
That means, BROWSER IS NOT GOING TO THAT URL, if you wish to actually go forward to the long-url location, simply do something like:
document.location.href = longurl;
iirc, jquery doesn't trigger "click" on A elements. I'd try old good "location.href=whatever" in the callback.
bitlyJSON(function(shortUrl) {
link.attr("href", function() {
//process shortUrl
//return finalized url;
});
location.href = link.attr("href");
});
I think what you actually want is to return false; from the click event, to prevent the actual following of the href, right?
Like:
jQuery("#idofaelement").click(function(event) {
//event.preventDefault(); //stop the click action
var link = jQuery(this);
bitlyJSON(function(shortUrl) {
link.attr("href", function() {
//process shortUrl
//return finalized url;
}).unbind().click();
});
return false;
});
#Tzury Bar Yochay pointed me in the right direction by suggesting I use location.href to update the url. Also #Victor helped with his answer.
I got things kinda working combining the answers, but had issues with the history in firefox. Seems that updating window.location indeed redirected the user, but also removed the "source page" from the history. This did not happen in chrome, safari, ie8, ie8-compatibility or ie7.
Based on this response to another question I was able to create a workaround giving the following working code + made a few changes:
jQuery("#idofaelement").one("click", function(event) {
bitlyJSON(function(shortUrl) {
jQuery("#idofaelement").attr("href", function() {
//process shortUrl
//return finalized url;
});
setTimeout(function() {
window.location = jQuery("#idofaelement").attr("href");
}, 0);
});
return false;
});
Thanks for all the help!

Categories