Unable to Disable Browser Back button on some devices - javascript

I am working on an application where I want user to continue without clicking back button. I want to prevent them to go back and perform same task again and if they click back button then display a popup message with bootstrap modal.
Here is my code snippet.
function showPopup(url, valid_device) {
if (window.history && window.history.pushState) {
$(window).on('popstate', function() {
$('#bckBtnModal').modal('show');
});
$(window).on('DOMContentLoaded', function() {
$('#bckBtnModal').modal('show');
});
}
}
function moveForward(url){
window.history.pushState( 'forward', null, url );
history.forward()
}
function checkState(url, valid_device){
if (window.history && window.history.pushState) {
window.history.pushState({},'forward', null, url);
$(window).on('popstate', function() {
$('#bckBtnModal').modal('show');
});
$(window).on('DOMContentLoaded', function() {
if(valid_device && show_on_dom_event && push_state_enable){
$('#bckBtnModal').modal('show');
}
});
}
}
I have used above function like given below:
window.onpageshow = BackBtnHandler.showPopup('/reservations/guest_party_details', '<%= valid_device?%>')
window.onnavigate = BackBtnHandler.moveForward('/reservations/guest_party_details');
But it is not global solution, I found that on OSx chromium browser when page loaded two functions called because it fires two event popstate, DOMContentLoaded while the other most of the browsers either calls popstate or DOMContentLoaded.
I have also tried history.js but didn't get any useful from it.
Anyone have any global solution which works for all major browsers and for all major Operating systems?

Related

Chrome/Opera `window.onpopstate` bug

I'm trying to handle window.onpopstate event in my single page app and in Google Chrome and Opera it doesn't work.
This is my simple html page
<html>
<head>
<meta charset="utf-8" />
<script>
window.onpopstate = function(e) {
alert('onpopstate');
}
setTimeout(function () {
history.pushState(1, 'Hello John', '/hello/john');
}, 2000);
setTimeout(function () {
history.pushState(2, 'Hello Emily', '/hello/emily');
}, 3000);
</script>
</head>
<body></body>
</html>
When I hit browser's back button I expect to see alert with onpopstate text to be shown. In Safari, Firefox, Vivaldi it works as expected. In Chrome and Opera it never called, it is just going to previous page by reloading it which is bad scenario for my SPA.
What is more weird is that I found some trick to make it work:
go to dev tools, console tab
simply execute console.log(history) or even console.log(window)
and it magically starts working! But if I do the same in script on the page with or without timeout this trick doesn't work at all. So the only way to make onpopstate work which I found is to manually go to console and execute console.log(history).
Maybe I'm missing something? Any help would be appreciated.
My environment:
macOS 11.0.1 (20B29)
Chrome 87.0.4280.67 (Official Build) (x86_64)
Opera 72.0.3815.378
Posted Chromium bug
SOLUTION:
Ok, this is not a bug, it is a feature! It is kinda interact first feature of Chromium. If user interact first somehow with a page then everything gonna work, otherwise back button will go back with reloading.
So e.g. just add some button into body and click on it first, then try to click back in browser.
<body>
<button onclick="alert('hello');">alert</button>
</body>
To be able to run some javascript like moving on history or play sounds or pop up windows the user first hast to interact (click) on the web site.
you can find more info here:
WebUpdates 2017
There's note I found on MDN. It says:
Note: Calling history.pushState() or history.replaceState() won't
trigger a popstate event. The popstate event is only triggered by
performing a browser action, such as clicking on the back button (or
calling history.back() in JavaScript), when navigating between two
history entries for the same document.
You can learn more here.
And I suggest you to register your event handler to the addEventListener() instead of registering it to onpopstate property of WindowEventHandlers.
Example:
<html>
<head>
<meta charset="utf-8" />
<script>
// ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event
window.addEventListener('popstate', function(e) {
alert('onpopstate');
}
setTimeout(function () {
history.pushState(1, 'Hello John', '/hello/john');
}, 2000);
setTimeout(function () {
history.pushState(2, 'Hello Emily', '/hello/emily');
}, 3000);
</script>
</head>
<body></body>
</html>
Use an Object for the state. From MDN
The state object is a JavaScript object which is associated with the
new history entry created by pushState(). Whenever the user navigates
to the new state, a popstate event is fired, and the state property of
the event contains a copy of the history entry's state object.
const log = Logger();
// create 3 'history pages'
history.pushState({page: 1, greet: "Hello John"}, '', "#John");
history.pushState({page: 2, greet: "Hello Emily"}, '', "#Emily");
history.pushState({page: 3, greet: "Hello James"}, '', "#James");
log(`current history state: ${JSON.stringify(history.state)}`);
// on popstate log the history state if applicable
window.addEventListener("popstate", () =>
history && history.state && log(`${history.state.greet || ""} #page ${
history.state.page} (location: ${location.hash})`)
);
// listener for the buttons
document.addEventListener("click", evt => {
if (evt.target.nodeName === "BUTTON") {
return evt.target.id === "back" ? history.back() : history.forward();
}
});
// helper function for logging in the document
function Logger() {
let logEl;
if (typeof window === "object") {
logEl = document.querySelector("#log") || (() => {
document.body.append(
Object.assign(document.createElement('pre'),
{id: "log"}));
return document.querySelector("#log");
})();
return (...logLines) =>
logLines.forEach(s => logEl.textContent += `${s}\n`);
} else {
return (...logLines) =>
logLines.forEach(ll => console.log(`* `, ll));
}
}
<button id="back"><< 1 back</button>
<button id="forward">1 forward >></button>

How to handle desktop notification in multiple tabs [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a way to detect if a browser window is not currently active?
I have a function that is called every second that I only want to run if the current page is in the foreground, i.e. the user hasn't minimized the browser or switched to another tab. It serves no purpose if the user isn't looking at it and is potentially CPU-intensive, so I don't want to just waste cycles in the background.
Does anyone know how to tell this in JavaScript?
Note: I use jQuery, so if your answer uses that, that's fine :).
In addition to Richard Simões answer you can also use the Page Visibility API.
if (!document.hidden) {
// do what you need
}
This specification defines a means for site developers to
programmatically determine the current visibility state of the page in
order to develop power and CPU efficient web applications.
Learn more (2019 update)
All modern browsers are supporting document.hidden
http://davidwalsh.name/page-visibility
https://developers.google.com/chrome/whitepapers/pagevisibility
Example pausing a video when window/tab is hidden https://web.archive.org/web/20170609212707/http://www.samdutton.com/pageVisibility/
You would use the focus and blur events of the window:
var interval_id;
$(window).focus(function() {
if (!interval_id)
interval_id = setInterval(hard_work, 1000);
});
$(window).blur(function() {
clearInterval(interval_id);
interval_id = 0;
});
To Answer the Commented Issue of "Double Fire" and stay within jQuery ease of use:
$(window).on("blur focus", function(e) {
var prevType = $(this).data("prevType");
if (prevType != e.type) { // reduce double fire issues
switch (e.type) {
case "blur":
// do work
break;
case "focus":
// do work
break;
}
}
$(this).data("prevType", e.type);
})
Click to view Example Code Showing it working (JSFiddle)
I would try to set a flag on the window.onfocus and window.onblur events.
The following snippet has been tested on Firefox, Safari and Chrome, open the console and move between tabs back and forth:
var isTabActive;
window.onfocus = function () {
isTabActive = true;
};
window.onblur = function () {
isTabActive = false;
};
// test
setInterval(function () {
console.log(window.isTabActive ? 'active' : 'inactive');
}, 1000);
Try it out here.
Using jQuery:
$(function() {
window.isActive = true;
$(window).focus(function() { this.isActive = true; });
$(window).blur(function() { this.isActive = false; });
showIsActive();
});
function showIsActive()
{
console.log(window.isActive)
window.setTimeout("showIsActive()", 2000);
}
function doWork()
{
if (window.isActive) { /* do CPU-intensive stuff */}
}
All of the examples here (with the exception of rockacola's) require that the user physically click on the window to define focus. This isn't ideal, so .hover() is the better choice:
$(window).hover(function(event) {
if (event.fromElement) {
console.log("inactive");
} else {
console.log("active");
}
});
This'll tell you when the user has their mouse on the screen, though it still won't tell you if it's in the foreground with the user's mouse elsewhere.
If you are trying to do something similar to the Google search page when open in Chrome, (where certain events are triggered when you 'focus' on the page), then the hover() event may help.
$(window).hover(function() {
// code here...
});

Disable browser back action using jquery

I am developing an online testing app and it is required that during the test, users cannot be allowed to refresh page neither go back until the test is ended. I have successfully been able to disable refresh action in jquery through all means possible (to the best of my knowledge) using the following code:
$(window).bind({
beforeunload: function(ev) {
ev.preventDefault();
},
unload: function(ev) {
ev.preventDefault();
}
});
But I have been having troubles disabling the back action on all browsers, the best solution I got on SO conflicts with the code I have above, it is given below:
window.onload = function () {
if (typeof history.pushState === "function") {
history.pushState("jibberish", null, null);
//alert("Reloaded");
window.onpopstate = function () {
history.pushState('newjibberish', null, null);
// Handle the back (or forward) buttons here
// Will NOT handle refresh, use onbeforeunload forthis.
};
}
else {
var ignoreHashChange = true;
window.onhashchange = function () {
if (!ignoreHashChange) {
ignoreHashChange = true;
window.location.hash = Math.random();
// Detect and redirect change here
// Works in older FF and IE9
// * it does mess with your hash symbol (anchor?) pound sign
// delimiter on the end of the URL
}
else {
ignoreHashChange = false;
}
};
}
}
The solution above suits my purpose in disabling the back button but conflicts with the page refresh prevention handler above.
I am out of ideas on what to do and I have also searched a long time for a solution to this but found none yet. Any help would be greatly appreciated, even if it takes a totally different approach to solving the problem, I wouldn't mind at all.
Thanks everyone
UPDATE
I never realized that doing things this way breaks a lot of ethical rules, anyway, I've thought about it and figured out something else to do when if the page is refreshed or back button pressed (either using keyboard or the browser controls). I want to redirect to a url which will end the current exam session. I believe that's possible, hence I think the solution I seek is to get the best way to achieve this. Redirecting to another url if back button or refresh button is pressed (both using the browser controls and the keyboard).
I have tried many options but none worked except this-
//Diable Browser back in all Browsers
if (history.pushState != undefined) {
history.pushState(null, null, location.href);
}
history.back();
history.forward();
window.onpopstate = function () {
history.go(1);
};
With regards to the update I posted in my question, I have been able to solve my problem. Here's what I did (just modifying my existing code a little and removing the window.onload listener I had initially):
$(window).bind({
beforeunload: function(ev) {
window.location.replace("my_url_goes_in_here");
},
unload: function(ev) {
window.location.replace("my_url_goes_in_here");
}
});
This construct works for both page refresh and back actions done in anyway (either using keyboard or browser controls for the any of them).
However, I've not yet tested in any other browser other than firefox 47.0, but I'm glad it's working for now all the same.
Thanks for all your comments, they were extremely helpful
Using javascript if you have two pages page1 and page2 and (page1 redirect to page2) and you want to restrict the user from getting back to page1, just put this code at page1.
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
function disableBack() {
window.history.forward()
}
window.onload = disableBack();
window.onpageshow = function (evt) {
if (evt.persisted)
disableBack()
}
});
</script>

Browser F11 Fullscreen Does Not Register with -webkit-full-screen or Javascript API

I am using javascript(for example, requestFullscreen) and css(:-webkit-full-screen) API's to detect the browser's state in full screen or not. I don't have a issue with these API's, as they work successfully.
The issue I am having is that if the user hits F11, it does not register in the browser's environment and the javascript api and CSS api's for fullscreen detection do not detect fullscreen. Is there any way to work around this? I have some animations that depend on the size of the screen(in regards to fullscreen) and I have come to a dead end.
Worth a look
(function() {
var checktimer;
function isFullScreen() {
return (window.fullScreen) || (window.innerWidth == screen.width && window.innerHeight == screen.height);
}
$(window).on('resize', function() {
if(typeof checktimer!="undefined") { clearTimeout(checktimer); }
checktimer = setTimeout(function() { alert(isFullScreen()); },2000);
});
})();
Notice that the call to the isFullScreen() function is timed, this is because some browsers will animate the fullscreen action , causing a resize event to fire until the animate stops.
Tried on Firefox and Chrome - ff uses the window.fullScreen here, I have not looked at the others, but easy enough to add those in the check of the return.
As #bigBen suggested in his comment above, $(window).on('resize', function() { }); does detect F11, for me too (Chrome Version 33.0.1750.154 m).
However, with the way I use it there are multiple calls because of the on('keydown'/'keyup'), so as long as you use off('keydown'/'keyup') right after, it'll run your functions once.*
*Correction The multiple calls were because of window.onresize, not keydown.
Here's a console screenshot where I logged entering and exiting via F11 using 'resize'
http://i.stack.imgur.com/4ojr1.png
Old code:
$(document).on('keydown', function(event) {
$(document).off('keydown');
$(window).on('resize', function() {
if ($('body').hasClass('fullscreenOn')) {
$('body').removeClass('fullscreenOn');
// Do functions when exiting fullscreen
$(document).on('keydown'); // Turn keydown back on after functions
console.log("Exit F11");
} else {
$('body').addClass('fullscreenOn');
// Do functions when entering fullscreen
$(document).on('keydown'); // Turn keydown back on after functions
console.log("Enter F11");
}
});
});
Jsfiddle (old): http://jsfiddle.net/4b8VL/ (Caveat - need to click inside result area before first F11 keypress)
Using 'resize' has the added benefit of also detecting when entering fullscreen via JS API. Do note that if you're using fullscreen via JS API as well, you should hide the button for going fullscreen (via the JS API) whenever fullscreen is activated. Thus the user can only use the exit method given by the browser - Esc when fullscreen entered via JS API, or F11 when fullscreen entered via F11.
* Update * The following code is cross-browser friendly. Instead of detecting window.onresize and using that as a basis for toggling fullscreen, it's better to just redirect the F11 key to use the fullscreen API.
// fullscreen API goes here - **MDN - Using fullscreen mode -** https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode
$(document).on('fullscreenchange', function(event) { // 'fullscreenchange' eventlistener from fullscreen API
event.preventDefault();
if ($('body').hasClass('fullscreen')) {
$('body').removeClass('fullscreen');
// Do functions when exiting fullscreen
console.log("Exit Fullscreen");
} else {
$('body').addClass('fullscreen');
// Do functions when entering fullscreen
console.log("Enter Fullscreen");
}
});
$(document).on('keydown', function(event) {
if (event.which == 122) {
event.preventDefault();
toggleFullScreen(); // From fullscreen API
}
});
Note: This doesn't appear to work in jsfiddle, due to the way jsfiddle is setup, I believe . However, it works in live sites; check out a side project of mine at http://aaronkhare.com/playground/circa (turn your volume down/off) for a live, working example of the new code.
You can use event listener fullscreenchange :
var isFullscreen = false;
document.addEventListener('fullscreenchange', function () {
isFullscreen = !!document.fullscreen;
yourFunction();
}, false);
document.addEventListener('mozfullscreenchange', function () {
isFullscreen = !!document.mozFullScreen;
yourFunction();
}, false);
document.addEventListener('webkitfullscreenchange', function () {
isFullscreen = !!document.webkitIsFullScreen;
yourFunction();
}, false);
function yourFunction() {
if(isFullscreen) {
$('.your-element').addClass('fullscreen');
// ...
} else {
$('.your-element').removeClass('fullscreen');
// ...
}
}
Documentation:
https://developer.mozilla.org/en-US/docs/Web/CSS/:fullscreen
https://developer.mozilla.org/en-US/docs/Web/Reference/Events/fullscreenchange
Check out this HTML5 Enter Fullscreen shim:
A shim is like a mini hack to make the action cross browser compatible (really a wedge or doorjam), until the browsers figure it out
// detecting if browser supports fullscreen
return document.body.mozRequestFullScreen
|| document.body.webkitRequestFullScreen
|| document.body.requestFullScreen;
// requesting full screen on an elm
( elm.mozRequestFullScreen && elm.mozRequestFullScreen() )
|| ( elm.webkitRequestFullScreen && elm.webkitRequestFullScreen() )
|| ( elm.requestFullScreen && elm.requestFullScreen() );
//binding to full screen event
( document.body.requestFullScreen &&
window.addEventListener('fullscreenchange',fullScreenEvent) ) || ( document.body.webkitRequestFullScreen &&
window.addEventListener('webkitfullscreenchange',fullScreenEvent ) ) || ( document.body.mozRequestFullScreen &&
window.addEventListener('mozfullscreenchange',fullScreenEvent) );
Source: https://gist.github.com/samccone/1653975/raw/0c5597e6bc8e312d0674ff25a67d3271916a957f/gistfile1.js
My solution was to listen for the F11 key being pressed, manually fire the fullscreen API requestFullscreen method, and return false:
// listen for fullscreenchange and keydown events
$(document).on({
'fullscreenchange': fullscreenToggled,
'keydown': documentKeydown
});
// if F11 has been pressed, call the fullscreen api requestFullscreen method
// and return false, disabling the default browser functionality
function documentKeydown(e) {
if ( e.originalEvent.code == 'F11' ) {
$('body')[0].requestFullscreen();
return false;
}
}
// check to see if body has the :fullscreen pseudo-class
// (which only gets detected when fullscreen is launched via the api)
function fullscreenToggled() {
if ( $('body').is(':fullscreen') ) {
// we are in fullscreen mode
} else {
// not in fullscreen mode
}
}
Admittedly this won't work if the user launches fullscreen mode from the browser menu, and has not been tested in anything other than Chrome (v84.0.4147.125) at the moment, but does the job for me.

JavaScript Fullscreen API plugin

I've found a plugin called screenfull.js and I'm wondering if it's possible to automatically open the page in fullscreen without clicking on a button.
This is an example of code for making the page fullscreen :
document.getElementById('#button').addEventListener('click', function() {
if ( screenfull ) {
screenfull.request();
} else {
// Ignore or do something else
}
});
Using their demo, you could just run the request on window load:
e.g.
window.onload = function() {
screenfull.request( $('#container')[0] );
};
[edit]
You could also run this with jQuery document ready...
E.g.
$(document).ready(function() {
screenfull.request( $('#container')[0] );
});
No, that is not possible. The requestFullScrenn() must be triggered by a direct user action (like a click) for security considerations. It's just the same as with popups.
Read https://wiki.mozilla.org/Security/Reviews/Firefox10/CodeEditor/FullScreenAPI and maybe https://wiki.mozilla.org/Gecko:FullScreenAPI for reference.
I use a trick...
I listen for any click on the body to activate.
Eg:
$('body').on('click', '*', function() {
screenfull.request();
});
N.B.: It does not track buttons (e.t.c) that already have event handlers...

Categories