Skip to content

Commit

Permalink
Show/hide modals when navigating with back/forward buttons
Browse files Browse the repository at this point in the history
Using pushState and popState event handlers, we can understand when the
modal needs to be closed (and prevents the browser from navigating
away), and when the modal needs to be reopened (using the new modal:reopen
event).

By default this is turned off, but can be enabled in the modal options
with `updateHistory: true`.

Note that as this stands, this doesn't work very well with Turbolinks:
I'm not sure yet if this is a bug on the Turbolinks side or not, but in
the meantime, this behaviour works for non-Turbolinks sites.
  • Loading branch information
soundasleep committed Aug 7, 2018
1 parent a229f49 commit 00cfc77
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,30 @@ Similar to how links can be automatically bound to open modals, they can be boun

_(Note that modals loaded with AJAX are removed from the DOM when closed)._

# Interacting with window.history

By default, jquery-modal will not interact with the browser history: if you open a modal dialog, then clicking
the back button will not close the modal, but will rather take you back to the previous page.

By setting the `updateHistory` option to `true`, and defining a `modal:reopen` event handler on your modal,
the browser history will be updated (using pushState/popState), and your users can use back/forward buttons.
For example:

```js
$("#open-modal").on('click', function() {
var target = $("#my-modal");
var options = {
// insert your other default options here
updateHistory: true,
};

target.on('modal:reopen', function() {
target.modal(options);
});
target.modal(options);
}
```
# Checking current state
* Use `$.modal.isActive()` to check if a modal is currently being displayed.
Expand All @@ -186,6 +210,7 @@ $.modal.defaults = {
showClose: true, // Shows a (X) icon/link in the top-right corner
modalClass: "modal", // CSS class added to the element being displayed in the modal.
blockerClass: "modal", // CSS class added to the overlay (blocker).
updateHistory: false, // Whether to update the browser history, enabling back/forward buttons (must implement `modal:reopen` event).

// HTML appended to the default spinner during AJAX requests.
spinnerHtml: '<div class="rect1"></div><div class="rect2"></div><div class="rect3"></div><div class="rect4"></div>',
Expand All @@ -208,6 +233,7 @@ $.modal.OPEN = 'modal:open'; // Fires after the modal has fin
$.modal.BEFORE_CLOSE = 'modal:before-close'; // Fires when the modal has been requested to close.
$.modal.CLOSE = 'modal:close'; // Fires when the modal begins closing (including animations).
$.modal.AFTER_CLOSE = 'modal:after-close'; // Fires after the modal has fully closed (including animations).
$.modal.REOPEN = 'modal:reopen'; // Fires when navigation needs a modal to re-open (see above).
```
The first and only argument passed to these event handlers is the `modal` object, which has three properties:
Expand Down
38 changes: 38 additions & 0 deletions jquery.modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@
this.$body.append(this.$elm);
this.open();
}

if (this.options.updateHistory) {
// we can only have a single popstate handler for the entire document, otherwise
// callbacks will start to be duplicated, and the UI may start having bugs
if (!$.modal.handlePopstate) {
// popstate gets the _after_ state, not the _before_ state
$.modal.handlePopstate = function(event) {
if (event.state.no_modals) {
$.modal.close();
} else if (event.state.modal) {
elm = $(event.state.modal);
elm.trigger($.modal.REOPEN, event);
}
};

$(window).on("popstate", function(jqueryEvent) {
$.modal.handlePopstate(jqueryEvent.originalEvent);
});
}

var copyState = history.state || {};
copyState.no_modals = true;
copyState.modal = null;
history.pushState(copyState, "", "");
}
};

$.modal.prototype = {
Expand Down Expand Up @@ -149,6 +174,17 @@
this.$elm.css('display', 'inline-block');
}
this.$elm.trigger($.modal.OPEN, [this._ctx()]);
if (this.options.updateHistory && this.$elm.length == 1) {
var id = this.$elm[0].id;
if (typeof id !== 'undefined') {
var copyState = history.state || {};
if (copyState.modal != "#" + id) {
copyState.no_modals = false;
copyState.modal = "#" + id;
history.pushState(copyState, "", ""); // ideally we'd set the location (third arg) to #id, but then we'd have to support opening modals on pageload from #id hash
}
}
}
},

hide: function() {
Expand Down Expand Up @@ -211,6 +247,7 @@
spinnerHtml: '<div class="rect1"></div><div class="rect2"></div><div class="rect3"></div><div class="rect4"></div>',
showSpinner: true,
showClose: true,
updateHistory: false,
fadeDuration: null, // Number of milliseconds the fade animation takes.
fadeDelay: 1.0 // Point during the overlay's fade-in that the modal begins to fade in (.5 = 50%, 1.5 = 150%, etc.)
};
Expand All @@ -223,6 +260,7 @@
$.modal.BEFORE_CLOSE = 'modal:before-close';
$.modal.CLOSE = 'modal:close';
$.modal.AFTER_CLOSE = 'modal:after-close';
$.modal.REOPEN = 'modal:reopen';
$.modal.AJAX_SEND = 'modal:ajax:send';
$.modal.AJAX_SUCCESS = 'modal:ajax:success';
$.modal.AJAX_FAIL = 'modal:ajax:fail';
Expand Down

0 comments on commit 00cfc77

Please sign in to comment.