A dialog over blank space really breaks the semantic connection between the dialog and the page it belongs to. Typical use cases for dialogs in web applications are asking questions, showing additional settings or filters, displaying an editor for some object, etc. In all of them the pop-up dialog only makes sense together with the page it is being opened from. Apart from that, users are accustomed to dialogs floating over their parent elements (like in Microsoft Windows or Office, for example).
So why does jQuery Mobile behave differently? I assume, the main reason is the way page cache works in jQuery mobile. In newer versions of jQuery Mobile any page can be opened as a dialog. To achieve this, you just need to add data-role="dialog" to the div, that represents the page. The idea of using pages for dialogs instead of a separate widget is pretty cool: it enables us to specify headers and footers, use the event system of pages, load dialogs via AJAX, etc. The problem in our case is, however, that dialogs behave exactly like pages: in particular, they can be automatically removed from the DOM, once they become inactive, to save memory. Since the dialog is a new page, the previous one becomes inactive and may now be removed. In fact, it is not always removed right away, but is made invisible by an overlay, so it can be removed any time. The result is, that if you just make the overlay behind the dialog semi-transparent by setting css opacity, it will still become blank after two or three page transitions,
Now, what do we need to do, to make the parent page of a dialog visible?
- Every time a dialog is opened:
- the overlay behind it needs to be made transparent;
- we need to ensure, that the parent page is not removed from the DOM while the dialog is still open.
- Every time a dialog is closed:
- everything we did to the parent page when opening the dialog must be undonde.
Trasnlated into jQuery mobile code, this makes the following few lines. Just insert them right after jQuery mobile is initialized and you will always get dialogs over greyed-out pages.
$(document).on('pagebeforeshow', 'div[data-dialog="true"]', function(event, ui) { ui.prevPage.addClass("ui-dialog-transparent-bg"); ui.prevPage.data("mobile-page").options.domCache = true; ui.prevPage.unbind("pagehide.remove"); }); $(document).on('pagehide', 'div[data-dialog="true"]', function(event, ui) { $('.ui-dialog-transparent-bg').removeClass("ui-dialog-transparent-bg"); ui.toPage.page( "option", "domCache", false ); ui.toPage.data("mobile-page").options.domCache = false; ui.toPage.bind("pagehide.remove"); });
You may ask yourself, why the regular api-method .page( "option", "domCache", false ) is not used to enforce caching when opening a dialog. The reason is, that in the curret Version of jQuery mobile (1.4.5) the option does not work as one would expect: According to this discussion, you cannot set the domCache option after the page had been created, so we need to unbind the pagehide.remove event additionally.
Note, that this approach works well even with chained dialogs. If you open a dialog from another one, that parent one will also be forced to stay in the cache untill the new one closes, and so on. Closing dialogs will reinstate the default behaviour on each page one after another. Of course, you should keep in mind, that this does require more memory than the default way of handling dialogs. It should not be much more, though, as dialog are mostly simple pages without too many elements. Still, avoid using many chained dialogs: apart from wasting memory they are also hard to track for the user.