7

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>
MAX POWER
  • 5,213
  • 15
  • 89
  • 141
  • 1
    Changing standard behaviour is terrible user experience. Why would you do such a thing? – Adrian Brand Mar 26 '19 at 22:36
  • 5
    Well you could argue this "standard behaviour" is bad UX - since a lot of users now (especially mobile app users) are accustomed to pressing back to dismiss popups and prompts. – MAX POWER Mar 26 '19 at 23:05
  • Well you would be arguing an incorrect argument. – Adrian Brand Mar 26 '19 at 23:08
  • What do you mean by `manually close the popup`? Do you mean closing the popup using `close` button with class `popup-close`? – pouyan Mar 27 '19 at 07:34
  • 1
    Even though your problem does not exist anymore or has been solved otherwise, you should follow up the answers posted. – Munim Munna Apr 03 '19 at 14:59
  • @AdrianBrand Would just like to mention that this functionality is key to our single most requested feature by our users. Suggesting something is terrible user experience in every case is worse advice than assuming it's always okay. It feels almost obnoxious to say this, but if your users request functionality and this is a requirement, that is by definition an improved UX – Nick Bull Feb 08 '21 at 01:12
  • Does this answer your question? [Close pop up on back button](https://stackoverflow.com/questions/25377383/close-pop-up-on-back-button) – Nico Haase May 30 '23 at 09:54

8 Answers8

13

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

Stefan Blamberg
  • 816
  • 9
  • 24
5

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.

sujumayas
  • 100
  • 8
  • look at the post here: https://ux.stackexchange.com/questions/100866/should-opening-a-modal-box-change-the-browser-history – user1031742 Jan 28 '20 at 22:04
1

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.

deblocker
  • 7,629
  • 2
  • 24
  • 59
0

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>
Lasithds
  • 2,161
  • 25
  • 39
0

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

Amy Shackles
  • 158
  • 1
  • 6
0

Just run window.history.back(); when closing the popup.

$('.popup-close').click(function() {
    hidePopup();
    window.history.back();
});
DaWe
  • 1,422
  • 16
  • 26
0

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.

  • Option 1 generates "Uncaught RangeError: Maximum call stack size exceeded" error in the console because of this, "$(window).off("beforeunload", hidePopup);" – user874737 May 07 '20 at 05:48
0

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');
}
alaa
  • 1
  • 1