Skip to content

Show/hide modals when navigating with back/forward buttons #229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
44 changes: 44 additions & 0 deletions jquery.modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,37 @@
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) {
if (typeof Turbolinks != 'undefined' && Turbolinks.supported) {
// do nothing; Turbolinks will be reloading the page anyway, unfortunately,
// so rather than hiding a modal (suggesting the page is ready) and then refreshing
// the page, we keep the modal displayed while the page reloads
} else {
$.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 +180,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 +253,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 +266,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