Thursday, December 20, 2012

PrimeFaces: working with big dialogs

I've come across a problem with PrimeFaces dialogs, when those dialogs contained elements which could expand, such as data grids. When those dialogs were bigger than the document itself, you could run into situation where the close button was unreachable and the user could only refresh page and lose all data.

The problem is that the dialog are not added to the main element, but are displayed as position:fixed. It means, that even if the scroll is available, the dialog itself won't be scrolled. That kind of positioning is working quite good with small dialogs, which are displayed only in the middle of window, but for big dialogs this is disaster. I didn't want to change that behaviour for small dialogs, for which it was working fine.

As a solution, I've created a JavaScript snippet, that checks if the dialog can fit the screen. If it is too big, the document is expanded and the dialog positioning is changed to absolute, so that the browser window scroll is displayed and each region of dialog can be visited. The position of dialog is also checked, to prevent the situation, where the dialog is displayed in unreachable region of window (this can happen, if you change the size of the browser after hiding the dialog.

This solution isn't perfect, it could be improved to react dynamically on window resize, for example, but this is fixing the main issue with too big dialogs on too small screens.

Any comments are welcomed.


function handleResizeDialog(dialog) {
    var el = $(dialog.jqId);
    var doc = $('body');
    var win = $(window);
    var elPos = '';
    var bodyHeight = '';
    var bodyWidth = '';
    // position:fixed is maybe cool, but it makes the dialog not scrollable on browser level, even if document is big enough
    if (el.height() > win.height()) {
        bodyHeight = el.height() + 'px';
        elPos = 'absolute';
    }   
    if (el.width() > win.width()) {
        bodyWidth = el.width() + 'px';
        elPos = 'absolute';
    }
    el.css('position', elPos);
    doc.css('width', bodyWidth);
    doc.css('height', bodyHeight);
    var pos = el.offset();
    if (pos.top + el.height() > doc.height())
        pos.top = doc.height() - el.height();
    if (pos.left + el.width() > doc.width())
        pos.left = doc.width() - el.width();
    var offsetX = 0;
    var offsetY = 0;
    if (elPos != 'absolute') {
        offsetX = $(window).scrollLeft();
        offsetY = $(window).scrollTop();
    }
    // scroll fix for position fixed
    if (pos.left < offsetX)
        pos.left = offsetX;
    if (pos.top < offsetY)
        pos.top = offsetY;
    el.offset(pos);
}