Search code examples
javascriptjqueryjquery-uijquery-validatebeautytips

jQuery BeautyTips is stacking bubbles together when referenced elements aren't in the view port


We're using BeautyTips as a the mechanism for creating various kinds of bubbles for communicating with the user. For example, we have a help icon that shows a bubble with more information. We also use BT to display error and warning messages.

Here's what this looks like in practice.

The bulk of the error messages are stacked on top of one another.

You can see that the error messages are stacked one on top of the other and wither their parent message.

In the form_validator I have a function for the errorPlacement argument:

    errorPlacement: function (er, el) {
        if (el && el.length > 0) {
            if (el.attr('id') == 'ContractOpenEnded') {
                createErrorBubble($('#contractDuration'), er.text())
            }
            else {
                createErrorBubble($(el), er.text());
            }
        }
    }

Here are the functions that do the heavy lifting to create these bubbles.

function createErrorBubble(element, text) {    
    // In a conversation with pcobar the spec was changed for the bubbles to be to the right of the element
    createBubble(element, text, 2, "none", "right", "#fe0000", "#b2cedc", "#ffffff", true);
    element.btOn();
}

Here the createBubble function.

function createBubble(element, content, messageType, trigger, positions, fillColor, strokeColor, foreColor, useShadow) {
    var btInstance = element.bt(
        content,
        {
            trigger: trigger,
            positions: positions,
            shrinkToFit: true,
            spikeLength: 12,
            showTip: function (box) {
                $(box).fadeIn(100);
            },
            hideTip: function (box, callback) {
                $(box).animate({ opacity: 0 }, 100, callback);
            },
            fill: fillColor,
            strokeStyle: strokeColor,
            shadow: useShadow,
            clickAnywhereToClose: false,
            closeWhenOthersOpen: false, // Closing when others open hides page validation errors. This should be false.
            preShow: function (box) {
                if (messageType != 1) return;

                var whiteboard = $($(box).children()[0]);
                var content = whiteboard.html();

                if (content.substr(0, 5).toLowerCase() == "<div>") {
                    content = content.substr(5, content.length -11);
                }

                whiteboard.html('<div><span class="helpWarningPrefix">Warning:</span> ' + content + "</div>");
            },
            cssStyles: { color: foreColor },
            textzIndex: 3602, // z-index for the text
            boxzIndex: 3601, // z-index for the "talk" box (should always be less than textzIndex)
            wrapperzIndex: 3600
        });
}

The answer to this quesytion is elluding me.

Update:

This actually looks like it might be more of an issue of trying to create the bubble for something that isn't in the viewable area of the page.

Edit: Updated the title to more clearly reflect the name of the problem.


Solution

  • With the realization from the update from earlier (the problem lying in elements off screen) I came up with a hack that "fixes" the problem but is inelegant and far from being the best thing since my cat got into the craft glitter and had no idea what had happened.

    Here's the hack:

    errorPlacement: function (er, el) {
        if (el && el.length > 0) {
            if (el.attr('id') == 'ContractOpenEnded') {
                $('#contractDuration').focus();
                createErrorBubble($('#contractDuration'), er.text())
            }
            else {
                $(el).focus();
                createErrorBubble($(el), er.text());
            }
        }
    },
    

    The magic here is the .focus() calls to bring the elements back into the view port.