diff --git a/README.md b/README.md index e999680..2d9c65c 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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: '
', @@ -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: diff --git a/jquery.modal.js b/jquery.modal.js index 02c522d..cb72199 100644 --- a/jquery.modal.js +++ b/jquery.modal.js @@ -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 = { @@ -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() { @@ -211,6 +253,7 @@ spinnerHtml: '', 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.) }; @@ -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';