Search code examples
javascriptjqueryevent-handlingmouseenterdynamic-html

Event handler not working on dynamic content even with event delegation


I have two ModalPopups The first one opens and has a Tooltip text that the user can view for more detail with the "mouseenter" event listener. Once the user clicks to close the modal through one of three buttons, the second ModalPopup opens with another Tooltip text. The Tooltip text on "mouseenter" does not work in this second ModalPopup. This is an entirely new Modalpopup although I have tried using the same modal and it still doesn't work.

So the flow is: User clicks button -> loading -> ModalPopup with tooltip -> User clicks 1 button out of 3 options -> Modalpopup closes -> loading -> ModalPopup number 2 with tooltip -> etc...

So it's loading dynamic content, then using delegation to set the tooltip listeners, then closing/deleting that content, then loading more dynamic content afterwards, and trying to again use delegated tooltip listeners for the second time.

I've tried removing the tooltip altogether on each modal load, unbinding everything on modal load before rebinding, unbinding in the tooltip code before rebinding, and etc.

Interestingly, when the second modalpopup opens, if I "Inspect Element" using chrome, the tooltips start working again.

Tootip Code:

(function ($) {
    'use strict';
    $.fn.extend({
        jqToolTip: function () {
            var opts = {
                tooltipDiv: 'tooltipbox',
                tooltipFadeOut: 'tooltipFadeOut',
                tooltipBoxTop: 'tooltipbox-top',
                tooltipBoxRight: 'tooltipbox-right',
                tooltipBoxLeft: 'tooltipbox-left',
                tooltipBoxBottom: 'tooltipbox-bottom',
                tooltipBoxRed: 'tooltipbox-red',
                tooltipBoxBottomRed: 'tooltipbox-bottom-red',
                tooltipBoxTopRed: 'tooltipbox-top-red',
                tooltipBoxLeftRed: 'tooltipbox-left-red',
                tooltipBoxRightRed: 'tooltipbox-right-red'
            };
            const classSelector = $(this).attr("class").split(/\s+/).find(x => x.includes("tooltip"));

            $(document.body).live('mouseenter', `.${classSelector}`, function (e) {
                var color = $(this).data("tooltip-color");
                var arrowOffsetx = $(this).data("tooltip-arrow-offset-x");
                var arrowOffsety = $(this).data("tooltip-arrow-offset-y");
                var helptext = $(this).data("tooltip");
                var direction = $(this).data("tooltip-direction");
                if (direction == null) direction = 'right';
                var top = $(this).offset().top;
                var left = $(this).offset().left;
                var offsetX = $(this).data("tooltip-offset-x");
                if (offsetX == null) offsetX = 0;
                var offsetY = $(this).data("tooltip-offset-y");
                if (offsetY == null) offsetY = 0;
                //console.log(top,left,$(this).width());
                var cssAdd = '';
                var cssColorAdd = '';
                $('.' + opts.tooltipDiv).removeClass(opts.tooltipFadeOut + ' '
                    + opts.tooltipBoxBottom + ' '
                    + opts.tooltipBoxRight + ' '
                    + opts.tooltipBoxTop + ' '
                    + opts.tooltipBoxLeft + ' '
                    + opts.tooltipBoxRed + ' '
                    + opts.tooltipBoxBottomRed + ' '
                    + opts.tooltipBoxTopRed + ' '
                    + opts.tooltipBoxLeftRed + ' '
                    + opts.tooltipBoxRightRed).html(helptext);
                switch (direction) {
                    case 'top': // top
                        top = top - 48 + offsetY;
                        left = left + $(this).width() / 2 - 25 + offsetX;
                        cssAdd = opts.tooltipBoxTop;
                        cssColorAdd = opts.tooltipBoxTopRed;
                        break;
                    case 'right': // right
                        top = top + $(this).height() / 2 - 13 + offsetY;
                        left = left + $(this).width() + 20 + offsetX;
                        cssAdd = opts.tooltipBoxRight;
                        cssColorAdd = opts.tooltipBoxRightRed;
                        break;
                    case 'bottom': // bottom
                        top = top + $(this).height() + 14 + offsetY;
                        left = left + $(this).width() / 2 - 28 + offsetX;
                        cssAdd = opts.tooltipBoxBottom;
                        cssColorAdd = opts.tooltipBoxBottomRed;
                        break;
                    case 'left': // left
                        top = top + $(this).height() / 2 - 13 + offsetY;
                        left = left - $('.' + opts.tooltipDiv).width() - 16 + offsetX;
                        cssAdd = opts.tooltipBoxLeft;
                        cssColorAdd = opts.tooltipBoxLeftRed;
                        break;
                }
                //console.log(left);
                if (color == 'red') {
                    $('.' + opts.tooltipDiv).addClass(opts.tooltipBoxRed);
                    $('.' + opts.tooltipDiv).addClass(cssColorAdd);
                }
                if (typeof(arrowOffsetx) != "undefined" && arrowOffsetx.length > 0) {
                    $('.' + opts.tooltipDiv)
                        .addClass('tooltipBoxArrowOffsetX')
                        .append('<style>.tooltipBoxArrowOffsetX::after,.tooltipBoxArrowOffsetX::before{left:' + arrowOffsetx + ' !important;}</style>')
                }
                if (typeof (arrowOffsety) != "undefined" && arrowOffsety.length > 0) {
                    $('.' + opts.tooltipDiv)
                        .addClass('tooltipBoxArrowOffsetY')
                        .append('<style>.tooltipBoxArrowOffsetY::after,.tooltipBoxArrowOffsetY::before{left:' + arrowOffsety + ' !important;}</style>');
                }
                $('.' + opts.tooltipDiv).addClass(cssAdd).css({ left: left, top: top }).fadeIn();
            })
            $(document.body).live('mouseleave', `.${classSelector}`, function (e) {
                $('.' + opts.tooltipDiv).addClass(opts.tooltipFadeOut);
            })
            $(document.body).live('click', `.${classSelector}`, function (e) {
                var ishide = $(this).data("tooltip-onclickhide") == '1';
                if (ishide) $('.' + opts.tooltipDiv).addClass(opts.tooltipFadeOut);
            })

            return this;
        },
    })
})(jQuery);

Solution

  • Event delegation for "mouseenter" and "mouseleave" events can only be done in the capturing phase.

    Replace

    $(document.body).live('mouseenter', `.${classSelector}`, function (e){}
    

    with

    document.body.addEventListener('mouseenter', function(e) {
        if (e.target.matches(`.${classSelector}`)) {
            // code here
            // use e.target instead of this
        }
    }, true);
    

    The same applies for "mouseleave".