I have a custom popup functionality. What I want is for the browser back button to close this popup.
My ideal scenario would be to NOT show a hashtag in the URL bar.
I have tried putting window.history.pushState('forward', null, ''); in my showPopup() function and then doing the following:
$(window).on('popstate', function () {
closePopup();
});
This does work but the problem is when I manually close the popup I have to press the back button twice to navigate back to the previous page (obviously because a browser history entry was added when the popup was opened).
What is the best way of doing this? Can it be done without adding a browser history entry? Essentially what I am trying to do is replicate the behaviour of a mobile app. Press the back button in a mobile app will usually dismiss any open modals or context menus.
$('.popup-link').click(function() {
showPopup();
});
$('.popup-close').click(function() {
hidePopup();
});
function showPopup() {
$('.popup').addClass('active');
}
function hidePopup() {
$('.popup').removeClass('active');
}
.popup {
background-color: #ccc;
width: 300px;
height: 300px;
display: none;
}
.popup.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="popup-link">Click</button>
<div class="popup">
<button class="popup-close">x</button>
<!-- popup content here -->
</div>
It is not possible to do it without adding browser history entries since you cannot override the back button behaviour, see Intercepting call to the back button in my AJAX application: I don't want it to do anything
Sujumayas answer is a good option, you should introduce some additional variable though to avoid problems with the history when opening multiple popups (e.g. when clicking the button multiple times)
Here is some possible sample code:
let popupOpen = false;
$(".popup-link").click(function() {
showPopup();
});
$(".popup-close").click(function() {
window.history.back();
});
function showPopup() {
if (popupOpen) {
window.history.back();
}
popupOpen = true;
window.history.pushState("forward", null, "");
$(".popup").addClass("active");
}
function hidePopup() {
popupOpen = false;
$(".popup").removeClass("active");
}
$(window).on("popstate", function() {
hidePopup();
});
Additionally please note that you might have problems with Opera Mini: https://caniuse.com/#search=history
Altho I don't recommend to override regular browser history managment (back button) to use it as you please....
I think that the only thing you missed in your example is that the close button should not close the modal by itself, but instead just execute a backbutton event (which will eventually close the modal).
That simple fix and it will work as you wanted.
I am doing already something like this, and it works nicely with the browser back-button and by pushing the android back-button as well. I am also not showing a hashtag in the URL bar.
Here is the stub (I just tried to apply that to Your scenario):
function freezeHistory() {
window.history.pushState({}, window.document.title, window.location.href);
}
function goBack() {
/*
Custom history back actions: close panel, close popup, close drop-down menu
*/
var popupOpen = $(".popup.active").length > 0;
if(popupOpen) {
hidePopup();
return false;
}
window.history.back();
return true;
}
function showPopup() {
$('.popup').addClass('active');
freezeHistory();
}
function hidePopup() {
$('.popup').removeClass('active');
}
$(window).on("popstate", function(e) {
/*
Browsers tend to handle the popstate event differently on page load.
Chrome (prior to v34) and Safari always emit a popstate event on page load,
but Firefox doesn’t.
*/
goBack();
})
If this won't work for You out-of-the box, it is because IMHO You may need to clarify a little bit how do You expect to manage the page history. Feel free to add more detail to Your question if this isn't working as You'd expect now, but anyway, I strongly believe You got the idea and You are able to apply it inside the scenario of Your web-app.
Open popup and try going back and forth with the browser history buttons
$(document).ready(function () {
// manage popup state
var poped = false;
$('.popup-link').click(function () {
// prevent unwanted state changtes
if(!poped){
showPopup();
}
});
$('.popup-close').click(function () {
// prevent unwanted state changtes
if(poped){
hidePopup();
}
});
function showPopup() {
poped = true;
$('.popup').addClass('active');
// push a new state. Also note that this does not trigger onpopstate
window.history.pushState({'poped': poped}, null, '');
}
function hidePopup() {
poped = false;
// go back to previous state. Also note that this does not trigger onpopstate
history.back();
$('.popup').removeClass('active');
}
});
// triggers when browser history is changed via browser
window.onpopstate = function(event) {
// show/hide popup based on poped state
if(event.state && event.state.poped){
$('.popup').addClass('active');
} else {
$('.popup').removeClass('active');
}
};
.popup {
background-color: #ccc;
width: 300px;
height: 300px;
display: none;
}
.popup.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="popup-link">Click</button>
<div class="popup">
<button class="popup-close">x</button>
<!-- popup content here -->
</div>
You could add window.history.go(-2) to your popstate. That should take you back twice, which would be your original page before the modal as pushState added an entry to your history object.
Conversely, you could use history.back(2)
Use window.location.href to go 2 pages back and reload
Just run window.history.back(); when closing the popup.
$('.popup-close').click(function() {
hidePopup();
window.history.back();
});
You would have two options to implement this:
Option 1: Using the window.beforeunload event. reference
$('.popup-link').click(function() {
showPopup();
$(window).on("beforeunload", hidePopup);
});
$('.popup-close').click(hidePopup);
function hidePopup() {
$(window).off("beforeunload", hidePopup);
$('.popup').removeClass('active');
}
Demo
Option 2: Using the HTML5 History API. reference
$('.popup-link').click(function() {
showPopup();
window.history.pushState('popup-open', null, '');
$(window).on('popstate', hidePopup);
});
$('.popup-close').click(function() {
if(history.state == 'popup-open') {
window.history.back();
}
hidePopup();
});
function hidePopup() {
$(window).off('popstate', hidePopup);
$('.popup').removeClass('active');
}
Demo
Edit: sujumayas's idea is also pretty good one.
Demo
Further, I'ld recommend to register the popstate / beforeunload events only when necessary and unregister them, when you no longer need 'em in order to avoid overhead.
if anyone use bootstrap with any version you can use this
let popupOpen = false;
//show
$(".popup-link").on('click',(function() {
showPopup();
})
);
// hide
$(".popup-close").on('click',(function() {
window.history.back();
})
);
// on click back button
$(window).on("popstate", function() {
if (popupOpen) {
hidePopup();
}
});
// for bootstrap, if clicked outside the modal or close somehow
$(window).on('hidden.bs.modal', function(e) {
// Make sure is open and the same modal
if (e.target.id=='exampleModal' && popupOpen) {
popupOpen = false;
window.history.back();
}
});
function showPopup() {
// if open back
if (popupOpen) {
window.history.back();
}
popupOpen = true;
// push the browser history
window.history.pushState("forward", null, "#popup");
// you have to use the id
$("#exampleModal").modal('show');
}
function hidePopup() {
popupOpen = false
// you have to use the id to close the modal
$("#exampleModal").modal('hide');
}
I've downloaded this Drupal 8 template and the site is at www.plotujeme.sk. It has an responsive navigation with this .js script:
function sidebar_menu() {
var windowsize = jQuerywindow.width(),
jQuerynav = jQuery("nav"),
slide = {
clear: function () {
jQuerybody.removeClass('toggled');
jQuery('.overlay').hide();
jQuery('.easy-sidebar-toggle').prependTo("header");
//jQuery('#search').prependTo("body");
jQuery('.navbar.easy-sidebar').removeClass('toggled');
jQuery('#navbar').removeAttr("style");
},
start: function () {
jQuery('.overlay').show();
jQuerybody.addClass('toggled');
jQueryhtml.addClass('easy-sidebar-active');
jQuerynav.addClass('easy-sidebar');
jQuery('.easy-sidebar-toggle').prependTo(".easy-sidebar");
//jQuery('#search').prependTo("#navbar");
jQuery('#navbar').height(jQuerywindow.height()).css({
"padding-top": "60px"
});
},
remove: function () {
jQuerynav.removeClass('easy-sidebar');
}
};
if (windowsize < 1003) {
jQuerynav.addClass('easy-sidebar');
jQuery('.easy-sidebar-toggle').on("click", function (e) {
e.preventDefault();
if (jQuerybody.hasClass('toggled')) {
slide.clear();
} else {
slide.start();
}
});
/*
jQueryhtml.on('swiperight', function () {
slide.start();
});
jQueryhtml.on('swipeleft', function () {
slide.clear();
}); */
} else {
slide.clear();
slide.remove();
}
}
and:
jQuery(document).ready(function () {
"use strict";
sidebar_menu();
jQuery(window).resize(function () {
sidebar_menu();
});
});
Problem is, that if I open responsive navigation by clicking on hamburger button, it works several times and then it stops working, the page and a browser freezes or is unresponsive for a long time. I also noticed that (even in template preview) sometimes it does not work at all and nothing happens after clicking hamburger icon. When I resize window multiple times sometimes it works sometimes not.
Do you see any error in the script that could possibly cause this problem?
Update: I also tried to use jQuery('.easy-sidebar-toggle').off("click"); just before jQuery('.easy-sidebar-toggle').on("click", function() {...}); but got the same results.
jQuery(window).resize(function () {
sidebar_menu();
});
As a result, whenever sidebar_menu function changes the window size, this function is called again and again, like a recursion, hence the freezing
I think the reason might be the following lines in the resize handler:
jQuerynav.addClass('easy-sidebar');
jQuery('.easy-sidebar-toggle').on("click", ...
They are run every time the window is resized by even one pixel, so a few dozen times a second if you drag the window border. Not sure about the first line, whether it adds the class over and over, but the second line certainly adds an event handler multiple times and fills up the stack. That's the reason your browser freezes. It just can't process the hundreds of registered events.
Just a guess, though.
I'm trying to make my footer disappear when on a mobile device and only when the keyboard is open. Which I have working perfectly, however the issue is that the footer reappears before the keyboard has time to close. Which is because I'm using the event from the textbox having focus not the keyboard being open. So I thought the best way to resolve this is with a .delay() however, this isn't working at all. Anyone have any ideas here?
<script>
var isMobileView = false; //global variable
$(document).ready(function () {
function setScreenWidthFlag() {
var newWindowWidth = $(window).width();
if ( $(window).width() > 600) {
isMobileView = false;
}
else {
isMobileView = true;
}
}
$(".tbinputArea").focus(function() {
if(isMobileView)
$("#footer").hide();
});
$(".tbinputArea").focusout(function() {
if(isMobileView)
$("#footer").delay(500).show();
});
setScreenWidthFlag();
$(window).on("resize", function (e) {
setScreenWidthFlag();
});
});
</script>
$("#footer").delay(500).show(0);
Try this.
refer this explanation precisely explained the reasons for it http://www.mattlunn.me.uk/blog/2012/06/jquery-delay-not-working-for-you/
Delay is just for queue delay not any event delay so try to add some events within like fadeIn or similar.
This is really the same question as this but I'm hoping somebody can answer it more thoroughly.
I'm trying to create a "mobile" and a "full" version of a site using two different js functions. I understand responsive design and I'm doing most of the changes through CSS but the two versions will need to have two different mutually exclusive js functions. Enquire.js allows the use of media queries and they do work well if the browser window is not resized. But if you resize the window from "full" to "mobile", the function full() in my example does not stop running despite my best (probably not very good) efforts below. The alerts are always correct however: "true" for full() and "false" for mobile(), the functions just seem to ignore the if statements. I'm okay with not using enquire if there's a better option.
js-decider.js
$(function() {
window['fullEnabled'];
window['mobileEnabled'];
enquire.register("screen and (min-width:680px)", {
match : function() {
window['fullEnabled'] = true;
window['mobileEnabled'] = false;
full();
}
}).register("screen and (max-width:679px)", {
match : function() {
window['fullEnabled'] = false;
window['mobileEnabled'] = true;
mobile();
}
}).listen();
});
full.js
function full() {
if (window['fullEnabled']==true) {
alert(window['fullEnabled']);
// do other stuff
}
}
mobile.js
function mobile() {
if (window['mobileEnabled']==true) {
alert(window['fullEnabled']);
// do other stuff
}
}
It looks like your media queries are causes the issue you have a cross over.
Try this instead:
enquire.register("screen and (max-width:679px)", {
match : function() {
//small screen
}
}).register("screen and (min-width:680px)", {
match : function() {
//large screen
}
}).listen();
Part of the page I'm developing requires a $(window).resize event to be added to a div when a user clicks a button, in order to toggle between resizing it with the window and leaving it fixed at its original size:
function startResize() {
$(window).resize(function() {
$("#content").width(newWidth);
$("#content").height(newHeight);
});
}
What I can't work out is how to "turn off" this event when the button is clicked again, so that the content stops resizing.
function endResize() {
// Code to end $(window).resize function
$("#content").width(originalWidth);
$("#content").height(originalHeight);
}
Any help with this would be greatly appreciated.
function endResize() {
$(window).off("resize");
$("#content").width(originalWidth);
$("#content").height(originalHeight);
}
Note that this is extremely obtrusive and might break other code.
This is better way:
function resizer() {
$("#content").width(newWidth);
$("#content").height(newHeight);
}
function startResize() {
$(window).resize(resizer);
}
function endResize() {
$(window).off("resize", resizer);
}
function startResize() {
$(window).on("resize.mymethod",(function() {
$("#content").width(newWidth);
$("#content").height(newHeight);
}));
}
function endResize() {
$(window).off("resize.mymethod");
}
using a namespace on the query method will allow to to turn off the resize event for you method only.