jeffjenx
jeffjenx

Reputation: 17487

jQuery events on dynamic elements don't work

I'm trying to create a custom tooltip by attaching hover event handlers to elements with a title attribute.

The code works on elements at page load, but when I try to incorporate dynamic elements, the tooltip is not disappearing when my mouse leaves the target element.

Show Tooltip

Urb.showTooltip = function() {
    var $element = $(this);
    var $tooltip = $element.data('tooltip');
    var $tip = $element.attr('title');

    console.log('show');

    if(!$tooltip) {
        // create a tooltip element
        $tooltip = $('<div />');
        $tooltip.addClass('tooltip');
        $tooltip.text($tip);
        Urb.$body.append($tooltip);

        // position the tooltip
        Urb.positionTooltip($tooltip, $element);
        $element.data('tooltip', $tooltip);

        // activate custom tooltip and deactivate browser tooltip
        $tooltip.addClass('active');
        $element.attr('tooltip', $tip);
        $element.removeAttr('title');
    } else {
        // position and activate custom tooltip
        Urb.positionTooltip($tooltip, $element);
        $tooltip.addClass('active');
    }
};

Hide Tooltip

Urb.hideTooltip = function() {
    var $element = $(this);
    var $tooltip = $element.data('tooltip');

    console.log('hide');

    if($tooltip){
        $tooltip.removeClass('active');
    }
};

Note: Urb is just a global object I've created as a namespace for my project.

So, nothing crazy going on there. I originally attached the tooltip logic to jQuery's .hover() function:

$('[title]').hover( Urb.showTooltip, Urb.hideTooltip );

That worked just fine, but now, when I try to attach the events to dynamic elements, the Urb.hideTooltip is not getting called at all, as it doesn't even log "hide" to my console.

Urb.$body.on('mouseover', '[title]', Urb.showTooltip);
Urb.$body.on('mouseleave', '[title]', Urb.hideTooltip);

Note: Urb.$body is reference to $('body')

Why doesn't the hideTooltip function get called? What am I missing?

Upvotes: 1

Views: 111

Answers (2)

Philip
Philip

Reputation: 2988

Solution 1

Use CSS-only :hover tooltips using :after pseudo-elements with content: attr( title ) ).

See: W3schools

Warning: this is not for all browsers out there, like IE, who gets left behind everytime... ;)

Solution 2

I've rewritten your code somewhat, to avoid referencing errors, etc. The only thing missing is the positioning function.

EDIT: Now it doesn't use a single tooltip <div>, but sets up tooltip <div>'s for each item on first hover, by removing the title and after using it to create the tooltip <div>.

Have a look:

var Urb = {};

Urb.showTooltip = function() {
    // Get current element
    var $element = $(this);

    // Get tooltip data (if it's there)
    var tooltipText = $element.data('tooltip');

    // Get title for backup
    var tip = $element.attr('title');

    console.log('show');

    // If title is still set, set it to tooltip data and remove title attribute
    if( typeof tip !== undefined && tip !== false) {
        // Remove title attribute
        $element.removeAttr('title');
        // Create new tooltip div
        var $newTip = $('<div />').addClass('tooltip').text( tip );
        // Append it to the element
        $element.append( $newTip );
    }

    $element.find('div.tooltip').addClass('active');
    
};


Urb.hideTooltip = function() {

    console.log('hide');

    // Empty tooltip div and deactivate it
    $(this).find('div.tooltip').removeClass('active');

};



var last_item_nr = 3;

function addItem() {
  var next_nr = ++last_item_nr;
  
  $elm = $('<div></div>');
  
  $elm
    .html('Item ' + next_nr + '<div class="tooltip">Tooltip for item ' + next_nr + '</div>' )
    .hover( Urb.showTooltip, Urb.hideTooltip );
  
  $('#items').append( $elm );
  
  console.log( 'Added item ' + next_nr );
}


$( document ).ready(function() {

  Urb.$body = $('body');

  $('#items > div').hover( Urb.showTooltip, Urb.hideTooltip );

});
#items > div {
    position: relative;
    float: left;
    clear: left;
    width: 200px;
    border: 1px solid #eee;
}

#items > div > .tooltip {
    position: absolute;
    top: 0px;
    left: 100%;
    background-color: #ccc;
    opacity: 0;
}

#items > div > .tooltip.active {
   opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button onclick="addItem();">Add item</button>
<div id="items">
  <div title="Tooltip for item 1">Item 1</div>
  <div title="Tooltip for item 2">Item 2</div>
  <div title="Tooltip for item 3">Item 3</div>
</div>

Upvotes: 2

jeffjenx
jeffjenx

Reputation: 17487

OMG, I can't believe I didn't think of this!!!

In my showTooltip function, I am removing the title attribute!

What an idiot!!

$element.removeAttr('title');

So, when the tooltip showed, the dynamic element check stopped working. I commented out that line of code and it works.

Now, I just have to figure out how to prevent the browser tooltip from showing.

Update:

Turns out I did need to remove the title attribute in order to disable browser tooltips. So, I made a few changes to my dynamic selector.

 // showTooltip
 $element.attr('data-title', $tip);
 $element.removeAttr('title');

 // window.load
 Urb.$body.on('mouseover', '[title], [data-title]', Urb.showTooltip);
 Urb.$body.on('mouseleave', '[title], [data-title]', Urb.hideTooltip);

Upvotes: 1

Related Questions