antd Pagination Component 'onChange' API don't call when browser back or fore - javascript

browser back or fore can't call 'onChange' API, so that the page list can't update according to current page
onPageChange = (page, pageSize) => {
const { dispatch } = this.props
const param = {
blogId: this.props.params.id,
pageIdx: page,
quantity: pageSize
}
dispatch(fetchIssues('getComments', param, ''))
hashHistory.push({
pathname: `/post/${this.props.params.id}`,
query: { pageIdx: page }
})
}
render() {
return <Pagination onChange={onPageChange} total={50} />
}

First, you need to take into account the browser back or fore click event. that event call popstate The popstate event is fired each time when the current history entry changes (user navigates to a new state). That happens when user clicks on browser's Back/Forward buttons or when history.back(), history.forward(), history.go() methods are programatically called.
in Javascript
window.addEventListener('popstate', function(event) var r = confirm("You pressed a Back button! Are you sure?!");
window.addEventListener('popstate', function(event) {
// The popstate event is fired each time when the current history entry changes.
if (r == true) {
// Call Back button programmatically as per user confirmation.
history.back();
// Uncomment below line to redirect to the previous page instead.
// window.location = document.referrer // Note: IE11 is not supporting this.
} else {
// Stay on the current page.
history.pushState(null, null, window.location.pathname);
}
history.pushState(null, null, window.location.pathname);
}, false);

Related

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");
}
});

How to capture back button of the browser without using pushstate?

I have tried capturing the back button action on the browser using pushstate and popstate but problem is pushstate alters the history, thus affecting the normal functioning of back button.
Most people recommend using:
window.onhashchange = function() {
if (window.innerDocClick) {
window.innerDocClick = false;
} else {
if (window.location.hash != '#undefined') {
goBack();
} else {
history.pushState("", document.title, window.location.pathname);
location.reload();
}
}
}
function goBack() {
window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
//blah blah blah
window.location.lasthash.pop();
}
Create an Array that collects the window.location.hash of the user with every change made to window.location.lasthash.
const updateHistory = (curr) => {
window.location.lasthash.push(window.location.hash);
window.location.hash = curr;
}
Unfortunately, there's no native event fired for clicking the browser's back button. However, there is a nice method to faking it using a combination of other native event listeners.
First, set a boolean that gets toggled each time the document is rendered by listening for the onmouseover event.
const onDocRenderHandler = (bool) => {
window.innerDocClick = bool;
}
document.onmouseover = onDocRenderHandler(true)
After the user clicks the back button, the document again renders so set the boolean flag to toggle again; only this time listening for the onmouseleave event to fire.
document.onmouseleave = onDocRenderHandler(false)
Finally, listen for theonhashchange event to fire in between document renders. This simulates a pseudo back button event for us to listen and act on.
const onHashChangeHandler = () => {
if(!window.innerDocClick) {
history.pushState("", document.title, window.location.pathname);
location.reload();
}
}
window.onhashchange = onHashChangeHandler()

How to Detect Browser Back Button event - Cross Browser

How do you definitively detect whether or not the user has pressed the back button in the browser?
How do you enforce the use of an in-page back button inside a single page web application using a #URL system?
Why on earth don't browser back buttons fire their own events!?
(Note: As per Sharky's feedback, I've included code to detect backspaces)
So, I've seen these questions frequently on SO, and have recently run into the issue of controlling back button functionality myself. After a few days of searching for the best solution for my application (Single-Page with Hash Navigation), I've come up with a simple, cross-browser, library-less system for detecting the back button.
Most people recommend using:
window.onhashchange = function() {
//blah blah blah
}
However, this function will also be called when a user uses on in-page element that changes the location hash. Not the best user experience when your user clicks and the page goes backwards or forwards.
To give you a general outline of my system, I'm filling up an array with previous hashes as my user moves through the interface. It looks something like this:
function updateHistory(curr) {
window.location.lasthash.push(window.location.hash);
window.location.hash = curr;
}
Pretty straight forward. I do this to ensure cross-browser support, as well as support for older browsers. Simply pass the new hash to the function, and it'll store it for you and then change the hash (which is then put into the browser's history).
I also utilise an in-page back button that moves the user between pages using the lasthash array. It looks like this:
function goBack() {
window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
//blah blah blah
window.location.lasthash.pop();
}
So this will move the user back to the last hash, and remove that last hash from the array (I have no forward button right now).
So. How do I detect whether or not a user has used my in-page back button, or the browser button?
At first I looked at window.onbeforeunload, but to no avail - that is only called if the user is going to change pages. This does not happen in a single-page-application using hash navigation.
So, after some more digging, I saw recommendations for trying to set a flag variable. The issue with this in my case, is that I would try to set it, but as everything is asynchronous, it wouldn't always be set in time for the if statement in the hash change. .onMouseDown wasn't always called in click, and adding it to an onclick wouldn't ever trigger it fast enough.
This is when I started to look at the difference between document, and window. My final solution was to set the flag using document.onmouseover, and disable it using document.onmouseleave.
What happens is that while the user's mouse is inside the document area (read: the rendered page, but excluding the browser frame), my boolean is set to true. As soon as the mouse leaves the document area, the boolean flips to false.
This way, I can change my window.onhashchange to:
window.onhashchange = function() {
if (window.innerDocClick) {
window.innerDocClick = false;
} else {
if (window.location.hash != '#undefined') {
goBack();
} else {
history.pushState("", document.title, window.location.pathname);
location.reload();
}
}
}
You'll note the check for #undefined. This is because if there is no history available in my array, it returns undefined. I use this to ask the user if they want to leave using a window.onbeforeunload event.
So, in short, and for people that aren't necessarily using an in-page back button or an array to store the history:
document.onmouseover = function() {
//User's mouse is inside the page.
window.innerDocClick = true;
}
document.onmouseleave = function() {
//User's mouse has left the page.
window.innerDocClick = false;
}
window.onhashchange = function() {
if (window.innerDocClick) {
//Your own in-page mechanism triggered the hash change
} else {
//Browser back button was clicked
}
}
And there you have it. a simple, three-part way to detect back button usage vs in-page elements with regards to hash navigation.
EDIT:
To ensure that the user doesn't use backspace to trigger the back event, you can also include the following (Thanks to #thetoolman on this Question):
$(function(){
/*
* this swallows backspace keys on any non-input element.
* stops backspace -> back
*/
var rx = /INPUT|SELECT|TEXTAREA/i;
$(document).bind("keydown keypress", function(e){
if( e.which == 8 ){ // 8 == backspace
if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
e.preventDefault();
}
}
});
});
You can try popstate event handler, e.g:
window.addEventListener('popstate', function(event) {
// The popstate event is fired each time when the current history entry changes.
var r = confirm("You pressed a Back button! Are you sure?!");
if (r == true) {
// Call Back button programmatically as per user confirmation.
history.back();
// Uncomment below line to redirect to the previous page instead.
// window.location = document.referrer // Note: IE11 is not supporting this.
} else {
// Stay on the current page.
history.pushState(null, null, window.location.pathname);
}
history.pushState(null, null, window.location.pathname);
}, false);
Note: For the best results, you should load this code only on specific pages where you want to implement the logic to avoid any other unexpected issues.
The popstate event is fired each time when the current history entry changes (user navigates to a new state). That happens when user clicks on browser's Back/Forward buttons or when history.back(), history.forward(), history.go() methods are programatically called.
The event.state is property of the event is equal to the history state object.
For jQuery syntax, wrap it around (to add even listener after document is ready):
(function($) {
// Above code here.
})(jQuery);
See also: window.onpopstate on page load
See also the examples on Single-Page Apps and HTML5 pushState page:
<script>
// jQuery
$(window).on('popstate', function (e) {
var state = e.originalEvent.state;
if (state !== null) {
//load content with ajax
}
});
// Vanilla javascript
window.addEventListener('popstate', function (e) {
var state = e.state;
if (state !== null) {
//load content with ajax
}
});
</script>
This should be compatible with Chrome 5+, Firefox 4+, IE 10+, Safari 6+, Opera 11.5+ and similar.
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
alert('hello world');
}
This is the only one solution that worked for me (it's not a onepage website).
It's working with Chrome, Firefox and Safari.
I had been struggling with this requirement for quite a while and took some of the solutions above to implement it. However, I stumbled upon an observation and it seems to work across Chrome, Firefox and Safari browsers + Android and iPhone
On page load:
window.history.pushState({page: 1}, "", "");
window.onpopstate = function(event) {
// "event" object seems to contain value only when the back button is clicked
// and if the pop state event fires due to clicks on a button
// or a link it comes up as "undefined"
if(event){
// Code to handle back button or prevent from navigation
}
else{
// Continue user action through link or button
}
}
Let me know if this helps. If am missing something, I will be happy to understand.
In javascript, navigation type 2 means browser's back or forward button clicked and the browser is actually taking content from cache.
if(performance.navigation.type == 2)
{
//Do your code here
}
Correct answer is already there to answer the question. I want to mention new JavaScript API PerformanceNavigationTiming, it's replacing deprecated performance.navigation.
Following code will log in console "back_forward" if user landed on your page using back or forward button. Take a look at compatibility table before using it in your project.
var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
console.log(perfEntries[i].type);
}
This will definitely work (For detecting back button click)
$(window).on('popstate', function(event) {
alert("pop");
});
My variant:
const inFromBack = performance && performance.getEntriesByType( 'navigation' ).map( nav => nav.type ).includes( 'back_forward' )
Browser: https://jsfiddle.net/Limitlessisa/axt1Lqoz/
For mobile control: https://jsfiddle.net/Limitlessisa/axt1Lqoz/show/
$(document).ready(function() {
$('body').on('click touch', '#share', function(e) {
$('.share').fadeIn();
});
});
// geri butonunu yakalama
window.onhashchange = function(e) {
var oldURL = e.oldURL.split('#')[1];
var newURL = e.newURL.split('#')[1];
if (oldURL == 'share') {
$('.share').fadeOut();
e.preventDefault();
return false;
}
//console.log('old:'+oldURL+' new:'+newURL);
}
.share{position:fixed; display:none; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.8); color:white; padding:20px;
<!DOCTYPE html>
<html>
<head>
<title>Back Button Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body style="text-align:center; padding:0;">
Share
<div class="share" style="">
<h1>Test Page</h1>
<p> Back button press please for control.</p>
</div>
</body>
</html>
See this:
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
it works fine...
I was able to use some of the answers in this thread and others to get it working in IE and Chrome/Edge. history.pushState for me wasn't supported in IE11.
if (history.pushState) {
//Chrome and modern browsers
history.pushState(null, document.title, location.href);
window.addEventListener('popstate', function (event) {
history.pushState(null, document.title, location.href);
});
}
else {
//IE
history.forward();
}
A full-fledged component can be implemented only if you redefine the API (change the methods of object ' history ')
I will share the class just written.
Tested on Chrome and Mozilla
Support only HTML5 and ECMAScript5-6
class HistoryNavigation {
static init()
{
if(HistoryNavigation.is_init===true){
return;
}
HistoryNavigation.is_init=true;
let history_stack=[];
let n=0;
let current_state={timestamp:Date.now()+n};
n++;
let init_HNState;
if(history.state!==null){
current_state=history.state.HNState;
history_stack=history.state.HNState.history_stack;
init_HNState=history.state.HNState;
} else {
init_HNState={timestamp:current_state.timestamp,history_stack};
}
let listenerPushState=function(params){
params=Object.assign({state:null},params);
params.state=params.state!==null?Object.assign({},params.state):{};
let h_state={ timestamp:Date.now()+n};
n++;
let key = history_stack.indexOf(current_state.timestamp);
key=key+1;
history_stack.splice(key);
history_stack.push(h_state.timestamp);
h_state.history_stack=history_stack;
params.state.HNState=h_state;
current_state=h_state;
return params;
};
let listenerReplaceState=function(params){
params=Object.assign({state:null},params);
params.state=params.state!==null?Object.assign({},params.state):null;
let h_state=Object.assign({},current_state);
h_state.history_stack=history_stack;
params.state.HNState=h_state;
return params;
};
let desc=Object.getOwnPropertyDescriptors(History.prototype);
delete desc.constructor;
Object.defineProperties(History.prototype,{
replaceState:Object.assign({},desc.replaceState,{
value:function(state,title,url){
let params={state,title,url};
HistoryNavigation.dispatchEvent('history.state.replace',params);
params=Object.assign({state,title,url},params);
params=listenerReplaceState(params);
desc.replaceState.value.call(this,params.state,params.title,params.url);
}
}),
pushState:Object.assign({},desc.pushState,{
value:function(state,title,url){
let params={state,title,url};
HistoryNavigation.dispatchEvent('history.state.push',params);
params=Object.assign({state,title,url},params);
params=listenerPushState(params);
return desc.pushState.value.call(this, params.state, params.title, params.url);
}
})
});
HistoryNavigation.addEventListener('popstate',function(event){
let HNState;
if(event.state==null){
HNState=init_HNState;
} else {
HNState=event.state.HNState;
}
let key_prev=history_stack.indexOf(current_state.timestamp);
let key_state=history_stack.indexOf(HNState.timestamp);
let delta=key_state-key_prev;
let params={delta,event,state:Object.assign({},event.state)};
delete params.state.HNState;
HNState.history_stack=history_stack;
if(event.state!==null){
event.state.HNState=HNState;
}
current_state=HNState;
HistoryNavigation.dispatchEvent('history.go',params);
});
}
static addEventListener(...arg)
{
window.addEventListener(...arg);
}
static removeEventListener(...arg)
{
window.removeEventListener(...arg);
}
static dispatchEvent(event,params)
{
if(!(event instanceof Event)){
event=new Event(event,{cancelable:true});
}
event.params=params;
window.dispatchEvent(event);
};
}
HistoryNavigation.init();
// exemple
HistoryNavigation.addEventListener('popstate',function(event){
console.log('Will not start because they blocked the work');
});
HistoryNavigation.addEventListener('history.go',function(event){
event.params.event.stopImmediatePropagation();// blocked popstate listeners
console.log(event.params);
// back or forward - see event.params.delta
});
HistoryNavigation.addEventListener('history.state.push',function(event){
console.log(event);
});
HistoryNavigation.addEventListener('history.state.replace',function(event){
console.log(event);
});
history.pushState({h:'hello'},'','');
history.pushState({h:'hello2'},'','');
history.pushState({h:'hello3'},'','');
history.back();
```
Here's my take at it. The assumption is, when the URL changes but there has no click within the document detected, it's a browser back (yes, or forward). A users click is reset after 2 seconds to make this work on pages that load content via Ajax:
(function(window, $) {
var anyClick, consoleLog, debug, delay;
delay = function(sec, func) {
return setTimeout(func, sec * 1000);
};
debug = true;
anyClick = false;
consoleLog = function(type, message) {
if (debug) {
return console[type](message);
}
};
$(window.document).click(function() {
anyClick = true;
consoleLog("info", "clicked");
return delay(2, function() {
consoleLog("info", "reset click state");
return anyClick = false;
});
});
return window.addEventListener("popstate", function(e) {
if (anyClick !== true) {
consoleLog("info", "Back clicked");
return window.dataLayer.push({
event: 'analyticsEvent',
eventCategory: 'test',
eventAction: 'test'
});
}
});
})(window, jQuery);
The document.mouseover does not work for IE and FireFox.
However I have tried this :
$(document).ready(function () {
setInterval(function () {
var $sample = $("body");
if ($sample.is(":hover")) {
window.innerDocClick = true;
} else {
window.innerDocClick = false;
}
});
});
window.onhashchange = function () {
if (window.innerDocClick) {
//Your own in-page mechanism triggered the hash change
} else {
//Browser back or forward button was pressed
}
};
This works for Chrome and IE and not FireFox. Still working to get FireFox right. Any easy way on detecting Browser back/forward button click are welcome, not particularly in JQuery but also AngularJS or plain Javascript.
I solved it by keeping track of the original event that triggered the hashchange (be it a swipe, a click or a wheel), so that the event wouldn't be mistaken for a simple landing-on-page, and using an additional flag in each of my event bindings. The browser won't set the flag again to false when hitting the back button:
var evt = null,
canGoBackToThePast = true;
$('#next-slide').on('click touch', function(e) {
evt = e;
canGobackToThePast = false;
// your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}
<input style="display:none" id="__pageLoaded" value=""/>
$(document).ready(function () {
if ($("#__pageLoaded").val() != 1) {
$("#__pageLoaded").val(1);
} else {
shared.isBackLoad = true;
$("#__pageLoaded").val(1);
// Call any function that handles your back event
}
});
The above code worked for me. On mobile browsers, when the user clicked on the back button, we wanted to restore the page state as per his previous visit.
Solution for Kotlin/JS (React):
import org.w3c.dom.events.Event
import kotlin.browser.document
import kotlin.browser.window
...
override fun componentDidMount() {
window.history.pushState(null, document.title, window.location.href)
window.addEventListener("popstate", actionHandler)
}
...
val actionHandler: (Event?) -> Unit = {
window.history.pushState(
null,
document.title,
window.location.href
)
// add your actions here
}
Was looking for a solution for this issue and put together a simple skeleton test html based on a few answers here and the MDN Web Doc pages for History.pushState() and WindowEventHandlers.onpopstate.
The following HTML and JavaScript is easy enough to copy and paste and test.
Works with back and forward browser buttons, shortcut keys, adds a change to the URL (which is important in some cases).
Simply enough to add to existing code key points and should be expandable too.
<html>
<body>
<div id="p1">Option 1</div>
<div id="p2">Option 2</div>
<div id="p3">Option 3</div>
<div id="p4">Option 4</div>
<div id="c"></div>
<script>
var chg={
set:function(str){
var d=document.getElementById("c");
d.textContent=str;
},
go:function(e){
var s={"p":this.id};
chg.set(s.p);
hstry.add(s);
}
};
var hstry={
add:function(s){
var u=new URL(window.location);
u.searchParams.set("x",s.p);
window.history.pushState(s,"",u);
},
adjust:function(state){
if(state.p){
chg.set(state.p);
}
}
};
window.onpopstate=function(e){
console.log("popstate, e.state:["+ JSON.stringify(e.state) +"]");
hstry.adjust(e.state);
}
window.onload=function(){
var i,d,a=["p1","p2","p3","p4"];
for(i=0;i<a.length;i++){
d=document.getElementById(a[i]);
d.addEventListener("click",chg.go,false);
}
}
</script>
</body>
</html>
browser will emit popstate event if you navigate through your app with calling
window.history.pushState({},'','/to')
If you manually enter the addresses into the address bar and click on the back button, popstate event will NOT be fired.
If you navigate in your app with this simplified function
const navigate = (to) => {
window.history.pushState({}, ",", to);
};
then this will work
const handlePopstate = () => {
console.log("popped");
};
window.addEventListener("popstate", handlePopstate);
I tried the above options but none of them is working for me. Here is the solution
if(window.event)
{
if(window.event.clientX < 40 && window.event.clientY < 0)
{
alert("Browser back button is clicked...");
}
else
{
alert("Browser refresh button is clicked...");
}
}
Refer this link http://www.codeproject.com/Articles/696526/Solution-to-Browser-Back-Button-Click-Event-Handli for more details

stop firing popstate on hashchange

I am working with the History API and using push and pop state. I want to stop the popstate event from firing in some scenario where I am only appending the hash to URL. for example in some cases on click of anchor it appends # to the URL and popstate is immediately fired) I want to avoid all the scenarios where # or #somehasvalue is appended to the URL and stop popstate from firing. I am mainitaing the URL's with query parameters and I do not have any scenario where I need the popstate event to fire with # in the URL.
Here is my code.
if (supportsHistoryApi()) {
window.onpopstate = function (event) {
var d = event.state || {state_param1: param1, state_param2: param2};
var pathName = window.location.pathname,
params = window.location.search;
loadData(event, pathName + params, d.state_param1, d.state_param2);
}
As far as I found you cannot stop the popstate from firing unfortunately.
What you can do is check for the event.state object. That will be null on a hash change.
So I'd suggest adding a
if(event.state === null) {
event.preventDefault();
return false;
}
To your popstate event handler at the very beginning.
I think that's the best way to prevent firing of the popstate handling code on your side when the hash changes, but i'd be interested to know if there are other solutions.
I have a solution!
When popstate event calls, check the pathname if it has changed, or just the hash changed. Watch below how it works for me:
window.pop_old = document.location.pathname;
window.pop_new = '';
window.onpopstate = function (event) {
window.pop_new = document.location.pathname;
if(pop_new != pop_old){
//diferent path: not just the hash has changed
} else {
//same path: just diferent hash
}
window.pop_old = pop_new; //save for the next interaction
};
In case someone still need this:
var is_hashed = false;
$(window).on('hashchange', function() {
is_hashed = true;
});
window.addEventListener('popstate', function(e){
// if hashchange
if (is_hashed) {
e.preventDefault();
// reset
is_hashed = false;
return false;
}
// Your code to handle popstate here
..................
});
This is pretty simple.
To prevent the popstate event to fire after you click a link with hash you have to eliminate the concequence of clicking the link - which is the addition of the hash to the browser address bar.
Basically you have to create the handler for click event, check if the click is on the element you whant to prevent a hash to appear in the URL and prevent hash to appear by calling event.preventDefault(); in the handler.
Here is the code example:
/**
* This your existing `onpopstate` handler.
*/
window.onpopstate = function(e) {
// You could want to prevent defaut behaviour for `popstate` event too.
// e.preventDefault();
// Your logic here to do things.
// The following reload is just an example of what you could want to make
// in this event handler.
window.location.reload();
};
/**
* This is the `click` event handler that conditionally prevents the default
* click behaviour.
*/
document.addEventListener('click', function(e) {
// Here you check if the clicked element is that special link of yours.
if (e.target.tagName === "A" && e.target.hash.indexOf('#the-link-i-want-make-discernable') > -1) {
// The 'e.preventDefault()' is what prevent the hash to be added to
// the URL and therefore prevents your 'popstate' handler to fire.
e.preventDefault();
processMySpecialLink(e, e.target);
}
});
/**
* Just an example of the link click processor in case you want making something
* more on the link click (e.g. smooth scroll to the hash).
*/
function processMySpecialLink(e, target) {
// Make things here you want at user clicking your discernable link.
}
Here is the matching HTML markup:
<!-- Somewhere in the markup -->
<span id="the-link-i-want-make-discernable"></span>
<!-- Some markup -->
My Special Link
<!-- Another markup -->
Any other link
This all does what is described above: prevents the default behaviour for a special hash link. As a side effect it makes no popstate event to fire as no hash is added to URL for the special case of clicking the #the-link-i-want-make-discernable hash link.
I had the same problem in SPA.
I fixed this by checking if current URL and new URL are the same - technically I don't prevent popstate but I prevent fetch if hash only changed.
So, I get current URL when page loaded. It must be var to be global:
var currentURL = window.location.origin + window.location.pathname + window.location.search;
Then when history change event fired (click by link, click native browser buttons 'back' 'forward') I check if current URL and new URL are the same (hash ignored).
If URLs are the same I make return, otherwise I update current URL.
let newURL = window.location.origin + window.location.pathname + window.location.search;
if ( currentURL == newURL ) return;
currentURL = newURL;
Additionally you can to see controller code. I added it to be able stop loading when user click fast a few times 'back' or 'forward' so a few requests starter - but I need to load last one only.
Full solution of load html file when URL changed (ignore when hash only changed), with push to history, and workable 'back' and 'forward' buttons.
// get current URL when page loaded.
var currentURL = window.location.origin + window.location.pathname + window.location.search;
// function which load html.
window.xhrRequestDoc = ( currentLink, pushToHistory = true, scrollTo = 'body' ) => {
if ( pushToHistory ) {
history.pushState( null, null, currentLink );
}
// get new URL
let newURL = window.location.origin + window.location.pathname + window.location.search;
// return if hash only changed
if ( currentURL == newURL ) return;
// update URL
currentURL = newURL;
document.body.classList.add( 'main_loading', 'xhr_in_progress' );
// create controler to stop loading - used when user clicked a few times 'back' or 'forward'.
controller = new AbortController();
const signal = controller.signal;
fetch( currentLink, { signal: signal })
.then( response => response.text() )
.then( function( html ) {
// initialize the DOM parser and parse as html
let parser = new DOMParser();
let doc = parser.parseFromString( html, "text/html" );
// insert data and classes to 'body'
document.body.innerHTML = doc.querySelector( 'body' ).innerHTML;
} );
}
window.addEventListener( 'popstate', () => {
// if user clicked a few times 'back' or 'forward' - process last only
if ( document.querySelector( '.xhr_in_progress' ) ) controller.abort();
// run xhr
xhrRequestDoc( location.href, false );
})

How can I stop the browser back button using JavaScript?

I am doing an online quiz application in PHP. I want to restrict the user from going back in an exam.
I have tried the following script, but it stops my timer.
What should I do?
The timer is stored in file cdtimer.js.
<script type="text/javascript">
window.history.forward();
function noBack()
{
window.history.forward();
}
</script>
<body onLoad="noBack();" onpageshow="if (event.persisted) noBack();" onUnload="">
I have the exam timer which takes a duration for the exam from a MySQL value. The timer starts accordingly, but it stops when I put the code in for disabling the back button. What is my problem?
There are numerous reasons why disabling the back button will not really work. Your best bet is to warn the user:
window.onbeforeunload = function() { return "Your work will be lost."; };
This page does list a number of ways you could try to disable the back button, but none are guaranteed:
http://www.irt.org/script/311.htm
It is generally a bad idea overriding the default behavior of the web browser. A client-side script does not have the sufficient privilege to do this for security reasons.
There are a few similar questions asked as well,
How can I prevent the backspace key from navigating back?
How can I prevent the browser's default history back action for the backspace button with JavaScript?
You can-not actually disable the browser back button. However, you can do magic using your logic to prevent the user from navigating back which will create an impression like it is disabled. Here is how - check out the following snippet.
(function (global) {
if(typeof (global) === "undefined") {
throw new Error("window is undefined");
}
var _hash = "!";
var noBackPlease = function () {
global.location.href += "#";
// Making sure we have the fruit available for juice (^__^)
global.setTimeout(function () {
global.location.href += "!";
}, 50);
};
global.onhashchange = function () {
if (global.location.hash !== _hash) {
global.location.hash = _hash;
}
};
global.onload = function () {
noBackPlease();
// Disables backspace on page except on input fields and textarea..
document.body.onkeydown = function (e) {
var elm = e.target.nodeName.toLowerCase();
if (e.which === 8 && (elm !== 'input' && elm !== 'textarea')) {
e.preventDefault();
}
// Stopping the event bubbling up the DOM tree...
e.stopPropagation();
};
}
})(window);
This is in pure JavaScript, so it would work in most of the browsers. It would also disable the backspace key, but that key will work normally inside input fields and textarea.
Recommended Setup:
Place this snippet in a separate script and include it on a page where you want this behavior. In the current setup it will execute the onload event of the DOM which is the ideal entry point for this code.
Working DEMO!
It was tested and verified in the following browsers,
Chrome.
Firefox.
Internet Explorer (8-11) and Edge.
Safari.
I came across this, needing a solution which worked correctly and "nicely" on a variety of browsers, including Mobile Safari (iOS 9 at time of posting). None of the solutions were quite right. I offer the following (tested on Internet Explorer 11, Firefox, Chrome, and Safari):
history.pushState(null, document.title, location.href);
window.addEventListener('popstate', function (event)
{
history.pushState(null, document.title, location.href);
});
Note the following:
history.forward() (my old solution) does not work on Mobile Safari --- it seems to do nothing (i.e., the user can still go back). history.pushState() does work on all of them.
the third argument to history.pushState() is a url. Solutions which pass a string like 'no-back-button' or 'pagename' seem to work OK, until you then try a Refresh/Reload on the page, at which point a "Page not found" error is generated when the browser tries to locate a page with that as its URL. (The browser is also likely to include that string in the address bar when on the page, which is ugly.) location.href should be used for the URL.
the second argument to history.pushState() is a title. Looking around the web most places say it is "not used", and all the solutions here pass null for that. However, in Mobile Safari at least, that puts the page's URL into the history dropdown the user can access. But when it adds an entry for a page visit normally, it puts in its title, which is preferable. So passing document.title for that results in the same behaviour.
<script>
window.location.hash = "no-back-button";
// Again because Google Chrome doesn't insert
// the first hash into the history
window.location.hash = "Again-No-back-button";
window.onhashchange = function(){
window.location.hash = "no-back-button";
}
</script>
For restricting the browser back event:
window.history.pushState(null, "", window.location.href);
window.onpopstate = function () {
window.history.pushState(null, "", window.location.href);
};
This code will disable the back button for modern browsers which support the HTML5 History API. Under normal circumstances, pushing the back button goes back one step, to the previous page. If you use history.pushState(), you start adding extra sub-steps to the current page. The way it works is, if you were to use history.pushState() three times, then start pushing the back button, the first three times it would navigate back in these sub-steps, and then the fourth time it would go back to the previous page.
If you combine this behaviour with an event listener on the popstate event, you can essentially set up an infinite loop of sub-states. So, you load the page, push a sub-state, then hit the back button, which pops a sub-state and also pushes another one, so if you push the back button again it will never run out of sub-states to push. If you feel that it's necessary to disable the back button, this will get you there.
history.pushState(null, null, 'no-back-button');
window.addEventListener('popstate', function(event) {
history.pushState(null, null, 'no-back-button');
});
How to block coming backwards functionality:
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
None of the most-upvoted answers worked for me in Chrome 79. It looks like Chrome changed its behavior with respect to the Back button after version 75. See here:
https://support.google.com/chrome/thread/8721521?hl=en
However, in that Google thread, the answer provided by Azrulmukmin Azmi at the very end did work. This is his solution.
<script>
history.pushState(null, document.title, location.href);
history.back();
history.forward();
window.onpopstate = function () {
history.go(1);
};
</script>
The problem with Chrome is that it doesn't trigger onpopstate event
unless you make browser action ( i.e. call history.back). That's why
I've added those to script.
I don't entirely understand what he wrote, but apparently an additional history.back() / history.forward() is now required for blocking Back in Chrome 75+.
React
For modal component in React project, the open or close of the modal, controlling browser back is a necessary action.
The stopBrowserBack: the stop of the browser back button functionality, also get a callback function. This callback function is what you want to do:
const stopBrowserBack = callback => {
window.history.pushState(null, "", window.location.href);
window.onpopstate = () => {
window.history.pushState(null, "", window.location.href);
callback();
};
};
The startBrowserBack: the revival of the browser back button functionality:
const startBrowserBack = () => {
window.onpopstate = undefined;
window.history.back();
};
The usage in your project:
handleOpenModal = () =>
this.setState(
{ modalOpen: true },
() => stopBrowserBack(this.handleCloseModal)
);
handleCloseModal = () =>
this.setState(
{ modalOpen: false },
startBrowserBack
);
This is the way I could it accomplish it.
Weirdly, changing window.location didn't work out fine in Google Chrome and Safari.
It happens that location.hash doesn't create an entry in the history for Chrome and Safari. So you will have to use the pushstate.
This is working for me in all browsers.
history.pushState({ page: 1 }, "title 1", "#nbb");
window.onhashchange = function (event) {
window.location.hash = "nbb";
};
history.pushState(null, null, document.URL);
window.addEventListener('popstate', function () {
history.pushState(null, null, document.URL);
});
This JavaScript code does not allow any user to go back (works in Chrome, Firefox, Internet Explorer, and Edge).
This article on jordanhollinger.com is the best option I feel. Similar to Razor's answer but a bit clearer. Code below; full credits to Jordan Hollinger:
Page before:
<a href="/page-of-no-return.htm#no-back>You can't go back from the next page</a>
Page of no return's JavaScript:
// It works without the History API, but will clutter up the history
var history_api = typeof history.pushState !== 'undefined'
// The previous page asks that it not be returned to
if ( location.hash == '#no-back' ) {
// Push "#no-back" onto the history, making it the most recent "page"
if ( history_api ) history.pushState(null, '', '#stay')
else location.hash = '#stay'
// When the back button is pressed, it will harmlessly change the url
// hash from "#stay" to "#no-back", which triggers this function
window.onhashchange = function() {
// User tried to go back; warn user, rinse and repeat
if ( location.hash == '#no-back' ) {
alert("You shall not pass!")
if ( history_api ) history.pushState(null, '', '#stay')
else location.hash = '#stay'
}
}
}
<html>
<head>
<title>Disable Back Button in Browser - Online Demo</title>
<style type="text/css">
body, input {
font-family: Calibri, Arial;
}
</style>
<script type="text/javascript">
window.history.forward();
function noBack() {
window.history.forward();
}
</script>
</head>
<body onload="noBack();" onpageshow="if (event.persisted) noBack();" onunload="">
<H2>Demo</H2>
<p>This page contains the code to avoid Back button.</p>
<p>Click here to Goto NoBack Page</p>
</body>
</html>
This code was tested with the latest Chrome and Firefox browsers.
<script type="text/javascript">
history.pushState(null, null, location.href);
history.back();
history.forward();
window.onpopstate = function () { history.go(1); };
</script>
Try it with ease:
history.pushState(null, null, document.title);
window.addEventListener('popstate', function () {
history.pushState(null, null, document.title);
});
You can just put a small script and then check. It won't allow you to visit previous page.
This is done in JavaScript.
<script type="text/javascript">
function preventbackbutton() { window.history.forward(); }
setTimeout("preventbackbutton()", 0);
window.onunload = function () { null };
</script>
The window.onunload function fires when you try to visit back or previous page through browser.
Very simple and clean function to break the back arrow without interfering with the page afterward.
Benefits:
Loads instantaneously and restores original hash, so the user isn't distracted by URL visibly changing.
The user can still exit by pressing back 10 times (that's a good thing), but not accidentally
No user interference like other solutions using onbeforeunload
It only runs once and doesn't interfere with further hash manipulations in case you use that to track state
Restores original hash, so almost invisible.
Uses setInterval, so it doesn't break slow browsers and always works.
Pure JavaScript, does not require HTML5 history, works everywhere.
Unobtrusive, simple, and plays well with other code.
Does not use unbeforeunload which interrupts user with modal dialog.
It just works without fuss.
Note: some of the other solutions use onbeforeunload. Please do not use onbeforeunload for this purpose, which pops up a dialog whenever users try to close the window, hit backarrow, etc. Modals like onbeforeunload are usually only appropriate in rare circumstances, such as when they've actually made changes on screen and haven't saved them, not for this purpose.
How It Works
Executes on page load
Saves your original hash (if one is in the URL).
Sequentially appends #/noop/{1..10} to the hash
Restores the original hash
That's it. No further messing around, no background event monitoring, nothing else.
Use It In One Second
To deploy, just add this anywhere on your page or in your JavaScript code:
<script>
/* Break back button */
window.onload = function(){
var i = 0;
var previous_hash = window.location.hash;
var x = setInterval(function(){
i++;
window.location.hash = "/noop/" + i;
if (i==10){
clearInterval(x);
window.location.hash = previous_hash;
}
}, 10);
}
</script>
In a modern browser this seems to work:
// https://developer.mozilla.org/en-US/docs/Web/API/History_API
let popHandler = () => {
if (confirm('Go back?')) {
window.history.back()
} else {
window.history.forward()
setTimeout(() => {
window.addEventListener('popstate', popHandler, {once: true})
}, 50) // delay needed since the above is an async operation for some reason
}
}
window.addEventListener('popstate', popHandler, {once: true})
window.history.pushState(null,null,null)
I had this problem with React (class component).
And I solved it easily:
componentDidMount() {
window.addEventListener("popstate", e => {
this.props.history.goForward();
}
}
I've used HashRouter from react-router-dom.
You simply cannot and should not do this. However, this might be helpful:
<script type = "text/javascript" >
history.pushState(null, null, 'pagename');
window.addEventListener('popstate', function(event) {
history.pushState(null, null, 'pagename');
});
</script>
This works in my Google Chrome and Firefox.
This seems to have worked for us in disabling the back button on the browser, as well as the backspace button taking you back.
history.pushState(null, null, $(location).attr('href'));
window.addEventListener('popstate', function () {
history.pushState(null, null, $(location).attr('href'));
});
Just run code snippet right away and try going back
history.pushState(null, null, window.location.href);
history.back();
window.onpopstate = () => history.forward();
<script src="~/main.js" type="text/javascript"></script>
<script type="text/javascript">
window.history.forward();
function noBack() {
window.history.forward();
}
</script>
Try this to prevent the backspace button in Internet Explorer which by default acts as "Back":
<script language="JavaScript">
$(document).ready(function() {
$(document).unbind('keydown').bind('keydown', function (event) {
var doPrevent = false;
if (event.keyCode === 8 ) {
var d = event.srcElement || event.target;
if ((d.tagName.toUpperCase() === 'INPUT' &&
(
d.type.toUpperCase() === 'TEXT' ||
d.type.toUpperCase() === 'PASSWORD' ||
d.type.toUpperCase() === 'FILE' ||
d.type.toUpperCase() === 'EMAIL' ||
d.type.toUpperCase() === 'SEARCH' ||
d.type.toUpperCase() === 'DATE' )
) ||
d.tagName.toUpperCase() === 'TEXTAREA') {
doPrevent = d.readOnly || d.disabled;
}
else {
doPrevent = true;
}
}
if (doPrevent) {
event.preventDefault();
}
try {
document.addEventListener('keydown', function (e) {
if ((e.keyCode === 13)) {
//alert('Enter keydown');
e.stopPropagation();
e.preventDefault();
}
}, true);
}
catch (err) {
}
});
});
</script>
It's basically assigning the window's "onbeforeunload" event along with the ongoing document 'mouseenter' / 'mouseleave' events so the alert only triggers when clicks are outside the document scope (which then could be either the back or forward button of the browser)
$(document).on('mouseenter', function(e) {
window.onbeforeunload = null;
}
);
$(document).on('mouseleave', function(e) {
window.onbeforeunload = function() { return "You work will be lost."; };
}
);
Just set location.hash="Something". On pressing the back button, the hash will get removed from the URL, but the page won't go back.
This method is good for preventing going back accidentally, but for security purposes you should design your backend for preventing reanswering.
Some of the solutions here will not prevent a back event from occurring - they let a back event happen (and data held about the page in the browsers memory is lost) and then they play a forward event to try and hide the fact that a back event just happened. Which is unsuccessful if the page held transient state.
I wrote this solution for React (when react router is not being used), which is based on vrfvr's answer.
It will truly stop the back button from doing anything unless the user confirms a popup:
const onHashChange = useCallback(() => {
const confirm = window.confirm(
'Warning - going back will cause you to loose unsaved data. Really go back?',
);
window.removeEventListener('hashchange', onHashChange);
if (confirm) {
setTimeout(() => {
window.history.go(-1);
}, 1);
} else {
window.location.hash = 'no-back';
setTimeout(() => {
window.addEventListener('hashchange', onHashChange);
}, 1);
}
}, []);
useEffect(() => {
window.location.hash = 'no-back';
setTimeout(() => {
window.addEventListener('hashchange', onHashChange);
}, 1);
return () => {
window.removeEventListener('hashchange', onHashChange);
};
}, []);
I create one HTML page (index.html). I also create a one (mechanism.js) inside a script folder / directory. Then, I lay all my content inside of (index.html) using form, table, span, and div tags as needed. Now, here's the trick that will make back / forward do nothing!
First, the fact that you have only one page! Second, the use of JavaScript with span / div tags to hide and display content on the same page when needed via regular links!
Inside 'index.html':
<td width="89px" align="right" valign="top" style="letter-spacing:1px;">
<small>
<b>
IN
</b>
</small>
[ <span id="inCountSPN">0</span> ]
</td>
Inside 'mechanism.js':
function DisplayInTrafficTable()
{
var itmsCNT = 0;
var dsplyIn = "";
for (i=0; i<inTraffic.length; i++)
{
dsplyIn += "<tr><td width='11'></td><td align='right'>" + (++itmsCNT) + "</td><td width='11'></td><td><b>" + inTraffic[i] + "</b></td><td width='11'></td><td>" + entryTimeArray[i] + "</td><td width='11'></td><td>" + entryDateArray[i] + "</td><td width='11'></td></tr>";
}
document.getElementById('inOutSPN').innerHTML =
"" +
"<table border='0' style='background:#fff;'><tr><th colspan='21' style='background:#feb;padding:11px;'><h3 style='margin-bottom:-1px;'>INCOMING TRAFFIC REPORT</h3>" +
DateStamp() +
" - <small><a href='#' style='letter-spacing:1px;' onclick='OpenPrintableIn();'>PRINT</a></small></th></tr><tr style='background:#eee;'><td></td><td><b>###</b></td><td></td><td><b>ID #</b></td><td></td><td width='79'><b>TYPE</b></td><td></td><td><b>FIRST</b></td><td></td><td><b>LAST</b></td><td></td><td><b>PLATE #</b></td><td></td><td><b>COMPANY</b></td><td></td><td><b>TIME</b></td><td></td><td><b>DATE</b></td><td></td><td><b>IN / OUT</b></td><td></td></tr>" +
dsplyIn.toUpperCase() +
"</table>" +
"";
return document.getElementById('inOutSPN').innerHTML;
}
It looks hairy, but note the function names and calls, embedded HTML, and the span tag id calls. This was to show how you can inject different HTML into same span tag on same page! How can Back/Forward affect this design? It cannot, because you are hiding objects and replacing others all on the same page!
How can we hide and display? Here goes:
Inside functions in ' mechanism.js ' as needed, use:
document.getElementById('textOverPic').style.display = "none"; //hide
document.getElementById('textOverPic').style.display = ""; //display
Inside ' index.html ' call functions through links:
<img src="images/someimage.jpg" alt="" />
<span class="textOverPic" id="textOverPic"></span>
and
Introduction
In my case this was a shopping order. So I disabled the button. When the user clicked back, the button was disabled still. When they clicked back one more time, and then clicked a page button to go forward. I knew their order was submitted and skipped to another page.
In the case when the page actually refreshed which would make the button (theoretically), available; I was then able to react in the page load that the order was already submitted and redirected then too.
This code is full javascript.
Put this on your home page or whatever you need when someon goes back it brings them to the page they were previously on.
<script type="text/javascript">
function preventBack() {
window.history.forward();
}
setTimeout("preventBack()", 0);
window.onunload = function () { null };
</script>

Categories

Resources