History API does not apply history changes until the user performs any actions on the page - javascript

There is a site page on which you need to implement routing using the history of API.
This page can be accessed as follows:
transition from a blank page,
transition from another page (external / internal),
return using the browser button Back,
routing the page inside using the open / close buttons.
Routing example:
site.com/typical-page/routing-page-level1/routing-page-level2/routing-page-level3/other-typical-page
typical-page, other-typical-page - pages without routing
routing-page-level1, routing-page-level2, routing-page-level3 - a page with routing.
Inside the page it works fine I go to the page with routing:
site.com/typical-page/routing-page-level1/
I click on the button level2, go to:
site.com/typical-page/routing-page-level1/routing-page-level2/
level3, go to:
site.com/typical-page/routing-page-level1/routing-page-level2/routing-page-level3
I click Close on the page or the BACK button in the browser I go
site.com/typical-page/routing-page-level1/routing-page-level2/
I click Close again on the page or the BACK button in the browser I go
site.com/typical-page/routing-page-level1/
If I immediately go to site.com/typical-page/routing-page-level1/routing-page-level2/routing-page-level3 from a blank page, and then click BACK in the browser, I return to a blank page.
But, if from a blank page I immediately go to site.com/typical-page/routing-page-level1/routing-page-level2/routing-page-level3, then I click anywhere on the page and then click BACK in the browser , then I’m going to site.com/typical-page/routing-page-level1/routing-page-level2, if again, then
at site.com/typical-page/routing-page-level1.
I understand that the browser does not apply history changes until the user performs any actions on the page. For example, click.
How can this be avoided?
This article was used as a sample JS - window.history - Delete a state
This is pseudo code.
let guideHistory = ['init'];
let flag;
history.pushState({state: '/guide'}, '', '/guide');
// User came from blank page
if (!history.state) {
setTimeout(() => {
flag = true;
history.replaceState({state: '/routing-page-level1'}, '', '/routing-page-level1');
level2.trigger('click');
}, 20);
}
// User came from other page
if (history.state) {
flag = true;
level2Click(level2);
}
level2.on('click', level2Click);
level3.on('click', level3Click);
function level2Click() {
addHistory(level2.attr("data-path"));
if (flag)) {
setTimeout(() => {
level3.trigger('click');
}, 30) ;
}
function addHistory(historyState, historyPath) {
if (guideHistory[0] === 'init'){
guideHistory.pop();
}
if (historyPath) {
guideHistory.push('/routing-page-level1' + '/' + historyPath);
}
else {
guideHistory.push('/routing-page-level1');
}
history.replaceState({state: historyState}, '', '/routing-page-level1/' + historyState);
}
}
function level3Click(event) {
setTimeout(() => {
addHistory(level3, level2));
}, 0);
}
window.onpopstate = function() {
if (guideHistory[0] !== 'init') {
if (!guideHistory.length) {
history.go(-1);
}
else {
pushHistory(guideHistory);
function pushHistory(guideHistory) {
let pop = guideHistory.pop();
history.pushState({state: pop}, '', pop);
}
}
}
};
Thanks!

Related

Run JavaScript function only if you're refreshing the page or coming to the page from external link

We have a Lottie animation that should act as a preloader and show only on the Home page.
We want to SHOW this when accessing the home page by:
clicking on a link from an external page (not on your website)
clicking refresh on the browser
when entering the URL in the browser's address bar.
We DON'T want to show the animation when
clicking on a link from an internal page (on your website)
navigate through the browser's prev/next history buttons.
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>
<div id="preloader">
<div class="logo" id="home-preloader"></div>
</div>
<style>
/* Some styling here */
</style>
<script>
function playPreloader() {
var animation = bodymovin.loadAnimation({
container: document.getElementById('home-preloader'),
path: 'preloader.json',
renderer: 'svg',
loop: false,
autoplay: true,
name: "Home Preloader",
});
}
</script>
Any ideas on how to do it? I tried a few things with PerformanceNavigation.type and PerformanceNavigationTiming.type but couldn't manage to figure it out. I'm not very skilled in JavaScript, but can manage things if I can have direction.
Even if this worked, it doesn't seem to differentiate between external and internal links.
window.addEventListener("load", function() {
var performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance;
var navigation = performance.getEntriesByType("navigation")[0];
if (navigation.type === "navigate") {
console.log("The page was accessed by following a link, a bookmark, a form submission, or a script, or by typing the URL in the address bar.");
}
else if (navigation.type === "reload") {
console.log("The page was accessed by clicking the Reload button or via the Location.reload() method.");
playPreloader();
yesPreloader();
}
else if (navigation.type === "back_forward") {
console.log("The page was accessed by navigating into the history.");
noPreloader();
}
else {
console.log("Any other way.");
}
});
After researching for two days, I found a comment that was very helpful and helped me create a working solution to my problem. Here's the code for anyone having the same problem.
If somebody can confirm that all of this is correct, that would be nice.
/* (0) WHEN THE PAGE IS LOADED */
window.addEventListener("load", function() {
/* (1) FIND HOW THE PAGE WAS ACCESSED */
var result;
var p;
if (window.performance.navigation) {
result = window.performance.navigation;
// 255
if (result == 255) {
result = 4
}
}
if (window.performance.getEntriesByType("navigation")) {
p = window.performance.getEntriesByType("navigation")[0].type;
// Page was accessed from a link or address bar
if (p == 'navigate') {
result = 0
}
// Page was reloaded (browser reload operation)
if (p == 'reload') {
result = 1
}
// Back or Forward (browser history)
if (p == 'back_forward') {
result = 2
}
// Prerender
if (p == 'prerender') {
result = 3
}
}
console.info(result);
/* (2) WHAT TO DO IN EACH CASE */
if (result == 0) {
// Page was accessed from a link or address bar
console.info("Page was accessed from a link or address bar");
console.info("Result was 0, result=" + result);
// Was it an internal link or (external link or address bar)
if (document.referrer.indexOf(location.hostname) !== -1) {
// Page was accessed from an internal link
console.info("Page was accessed from an internal link");
$(document).ready(function() {
noPreloader();
});
} else {
// Page was NOT accessed from internal link
// Probably accessed from external link or address bar
console.info("Page was NOT accessed from internal link. Probably accessed from an external link or address bar");
$(document).ready(function() {
$(this).scrollTop(0);
document.body.classList.add("overflow-x-hidden");
document.body.classList.add("overflow-y-hidden");
playPreloader();
yesPreloader();
});
}
} else if (result == 1) {
// Page was reloaded (browser reload operation)
console.info("Page was accessed by reloading (browser reload operation)");
console.info("Result was 1, result=" + result);
$(document).ready(function() {
$(this).scrollTop(0);
document.body.classList.add("overflow-x-hidden");
document.body.classList.add("overflow-y-hidden");
playPreloader();
yesPreloader();
});
} else if (result == 2) {
// Back or Forward (browser history)
console.info("Page was accessed from the browser history back or forward buttons");
console.info("Result was 2, result=" + result);
$(document).ready(function() {
noPreloader();
});
} else {
// Any other instance
console.info("Page was accessed: Any other instance (prerender or 255)");
console.info("Result was probably 255 (4) or prerender (3), result=" + result);
$(document).ready(function() {
noPreloader();
});
}
});
/* [END OF] (1) WHEN THE PAGE IS LOADED */

How can i execute a function after a form submission causes a page refresh?

I am using shopify's built in customer create, login, reset form submissions which on submit, forces the page to refresh. My intention is to show a message that shows after the page has been refreshed via a button click function. This is what i have so far; The message shows until that page refreshes and then the active class is removed as you would expect.
$(document).ready(function () {
class Alert {
constructor() {
this.customerAlert = document.createElement('div');
}
init(){
this.customerAlert.classList.add('customer-alert');
document.querySelector('body').append(this.customerAlert);
}
show(message){
this.customerAlert.textContent = message;
this.customerAlert.classList.add('active');
setTimeout(() => {
this.customerAlert.classList.remove('active');
}, 8000);
}
}
//create snackbar and initiate
const alertMessage = new Alert();
alertMessage.init();
const createAccountButton = document.querySelector('input.account-trigger');
createAccountButton.addEventListener('click', () => {
alertMessage.show('Your account in now under review');
});
});
Set a boolean variable in session storage just prior to the submit to represent the two states, and then read it in after the refresh.
Something like this:
function HandleFlag(){
var F=sessionStorage.getItem('Flag');
if(F=='1'){
// display your message box here
sessionStorage.setItem('Flag','0');
} else {
// the state is "0" so toggle it just before submitting
sessionStorage.setItem('Flag','1');
}
}
I hope you get my drift.

Back button handler not being registered when opening the app via push notification

I have an app written in Ionic v1 and AngularJS.
The app is using $ionicPlatform.registerBackButtonAction to handle the device's back button action.
Everything is working great when normally using the app. The problem is starting when the app is being opened by a push notification.
When the app is not in the background, and is being opened by clicking the push notification, the back button handler is not being registered and the physical back button throws me out of the app.
If I click anywhere on the screen (click, not scroll) then the touch event is being registered and everything is working well.
I have tried registering the event in multiple locations after the push notification event is launched.
I have tried simulating a touch event programaticlly on an empty space on the screen.
I have tried dispatching the resume event to simulate a return from paused state.
I have tried reloading the page programmaticlly after page loads.
Nothing seems to work.
The is my $ionicPlatform.registerBackButtonAction event handler that is being registered in app.run:
$ionicPlatform.registerBackButtonAction(function(e) {
e.preventDefault();
function showConfirm() {
var confirmPopup = $ionicPopup.show({
title : 'Close',
template : '<div>Do you want to close the app?</div>',
buttons : [
{
text : '<b>Cancel</b>'
},
{
text : '<b>Approve</b>',
type: 'button-positive',
onTap : function() {
ionic.Platform.exitApp();
}
}
]
});
};
// Is there a page to go back to?
if ($ionicHistory.backView()) {
// Go back in history
if($rootScope.pushPressed2) {
$location.path('/app/home').replace();
}
else {
$rootScope.backButtonPressed = true;
$ionicHistory.backView().go();
}
}
else {
if($rootScope.pushPressed2) {
$location.path('/app/home').replace();
}
else {
showConfirm();
}
}
return false;
}, 101);
$ionicPlatform.registerBackButtonAction(function(event)
{
if ($ionicHistory.currentStateName() === "home" )
{
$cordovaDialogs.confirm('Are You Sure You Want to Exit?', 'AppName', ['Cancel','OK'])
.then(function(buttonIndex) {
// no button = 0 'OK' = 1 'Cancel' = 2
var btnIndex = buttonIndex;
if(buttonIndex==2)
{
ionic.Platform.exitApp();
}
});
}
else
{
if($ionicHistory.currentStateName() === "login" )
{
ionic.Platform.exitApp();
}
else
{
$ionicHistory.goBack();
}
}
}, 100);

JS: Erroneous popstate event when trying to navigate back past the initial page load

I'm trying to implement JS history using pushState/popState. Navigating back and forward works just fine, but I have trouble navigating before the initial page load using the browser's back button. It needs 1 extra hit on the browser's back button to leave the page. Why is that?
function action(text) {
history.pushState({"text":text}, text);
doAction(text);
}
function doAction(text) {
$('span').text(text);
}
var $button = $('button');
var $p = $('p');
$p.hide();
action("foo");
$button.on('click', function(){
action("bar");
$button.hide();
$p.show();
})
window.addEventListener("popstate", function(e) {
if (e.state !== null) {
$button.show();
$p.text("Next back should navigate away from this page");
} else {
$p.text("Still here? Why is that? Next back will really navigate away");
}
});
https://jsfiddle.net/lilalinux/p8ewyjr9/20/
Edit: Tested with Chrome OS/X
The initial page load shouldn't use history.pushState because it would add another history entry. There is alredy an implicit first history item with state null.
Using history.replaceState for the initial page load, sets a state for that item but doesn't add another one.
var initialPageLoad = true;
function action(text) {
if (initialPageLoad) {
// replace the state of the first (implicit) item
history.replaceState({"text":text}, text);
} else {
// add new history item
history.pushState({"text":text}, text);
}
initialPageLoad = false;
doAction(text);
}
function doAction(text) {
$('span').text(text);
}
var $button = $('button');
var $p = $('p');
$p.hide();
action("foo");
$button.on('click', function(){
action("bar");
$button.hide();
$p.show();
})
window.addEventListener("popstate", function(e) {
if (e.state !== null) {
$button.show();
$p.text("Next back should navigate away from this page");
// } else {
// won't happen anymore, as the first item has state now
// $p.text("Still here? Why is that? Next back will really navigate away");
}
});

pushState Executing When Not Supposed To

I am using the HTML history API to manipulate the browser history stack to control navigation using JavaScript on my site. The function I have set up to change pages is working fine, but weirdly, when I added pushState to each individual page change, the pushState fires automatically, so that the last page change in the function is always the final one executed. This means the state being pushed is always the last page change statement.
The JavaScript:
// Change Page Function
function ChangeContent (page) {
var pages={"homepage":{title: "homepage"},"servicespage":{title: "servicespage"},"estimatespage":{title: "estimatespage"}};
HideNav();
//Show home page
for(var homepage in pages) {
if(page!==homepage) {
document.getElementById(homepage).style.display='none';
}
else {
document.getElementById(homepage).style.display='block';
history.pushState("homepage", "Home", "home" );
window.scrollTo(0,0);
}
}
//Show services page
for(var servicespage in pages) {
if(page!==servicespage) {
document.getElementById(servicespage).style.display='none';
}
else {
document.getElementById(servicespage).style.display='block';
history.pushState( "servicespage", "Our Services", "services");
window.scrollTo(0,0);
}
}
//Show estimates page
for(var estimatespage in pages) {
if(page!==estimatespage) {
document.getElementById(estimatespage).style.display='none';
}
else {
document.getElementById(estimatespage).style.display='block';
history.pushState( "estimatespage", "Get An Estimate", "estimates");
window.scrollTo(0,0);
}
}
}
When you run this code, instead of each individual page pushing it's state when that page is clicked, the state getting pushed is always the estimates page state. I've tried using else if statements instead, and I've tried embedding each for statement into self executing functions in the hope that it might solve a scope issue of some kind, but I've had no luck.
This is my onPopstate function:
// History State Change Display
window.onpopstate = function (event) {
var state = event.state;
console.log(state);
// Change History State Display To Home Page
if (state === "homepage") {
document.getElementById("homepage").style.display = 'block';
} else {
document.getElementById("homepage").style.display = 'none';
}
// Change History State Display To Services Page
if (state === "servicespage") {
document.getElementById("servicespage").style.display = 'block';
} else {
document.getElementById("servicespage").style.display = 'none';
}
// Change History State Display To Estimates Page
if (state === "estimatespage") {
document.getElementById("estimatespage").style.display = 'block';
} else {
document.getElementById("estimatespage").style.display = 'none';
}
};
For some reason each new page I click on has the URL extensions /estimatespage and then when I click the back button, it just cycles through the pages in reverse order in which they were written in the page change function, and properly updates the URL.
I'm not totally sure what the issue here is, but I think I'm pushing the state of all the pages automatically, which is weird, since the rest of the code is only executing when the proper page is selected.
Any help would be appreciated, as I think this is probably more obvious than I'm making it out to be.
HTML
<div id="nav">
<div class="headerlist" onclick="ChangeContent('homepage');">Home</div>
<div class="headerlist" onclick="ChangeContent('servicespage');">Services</div>
<div class="headerlist" onclick="ChangeContent('estimatespage');">Estimates</div>
</div>
<div id=homepage></div>
<div id=servicespage></div>
<div id=estimatespage></div>
You have three for loops in ChangeContent(). All three loops are testing for exactly the same condition -- merely with a renamed variable.
Keeping the style vaguely the same, you might try this instead, note that this single loop is to replace all three of those loops in ChangeContent():
for ( var p in pages ) {
if ( page !== p ) {
document.getElementById( p ).style.display = 'none';
}
else {
document.getElementById( p ).style.display = 'block';
if ( p === "homepage" )
history.pushState( "homepage", "Home", "home" );
else if ( p === "servicespage" )
history.pushState( "servicespage", "Our Services", "services");
else if ( p === "estimatespage" )
history.pushState( "estimatespage", "Get An Estimate", "estimates");
window.scrollTo( 0, 0 );
}
}

Categories

Resources