8

I'm showing a jQuery UI Tooltip right in the middle of my element, when hovering. But the tooltip itself disappears when hovering over it. (propagation problem)

Element:

enter image description here

With tooltip:

enter image description here

So, when I then hover the tooltip itself, it disappears, due to a propagation problem, I suppose.

HTML:

<div class="bar-wrapper">
    <label class="bar-lbl active one" title="2013"><span>2013</span></label>

    <div class="bar" data-percent="90"></div>
    <div class="bar-rest-overlay" data-percent="10"></div>
</div>

<div class="bar-wrapper">
    <label class="bar-lbl active one" title="2014"><span>2014</span></label>

    <div class="bar" data-percent="80"></div>
    <div class="bar-rest-overlay" data-percent="20"></div>
</div>

Current code:

$('.bar-lbl').tooltip(
{
    tooltipClass: 'bar-tooltip',
    position:
    {
        my: 'center',
        at: 'center'
    }
});

Partial fix (but leaves tooltips permanently visible):

$('.bar-lbl').on('mouseleave',
function(e)
{
    e.stopImmediatePropagation();

}).tooltip(
{
    tooltipClass: 'bar-tooltip',
    position:
    {
        my: 'center',
        at: 'center'
    }
});

Not working:

$('body').on('hover', '.ui-tooltip',
function(e)
{
    e.stopImmediatePropagation();
});

UPDATE: Thanks to Trevor, I came to a close-solution. (it still leaves the last hovered tooltip visible when hovering out):

It seems, when hovering out the tooltip itself, it hides. But hovering out of the .bar-lbl element, the tooltip stays visible, unless I hover another .bar-lbl element.

The problem is in the on('mouseleave') event on my .bar-lbl. I need both lines, but they interfere with each other. (see comments)

$('.bar-lbl').on('mouseenter',
function(e)
{
    $('.bar-lbl').not($(this)).tooltip('close');   // Close all other tooltips

}).on('mouseleave',
function(e)
{
    e.stopImmediatePropagation();    // keeps tooltip visible when hovering tooltip itself
    $('.bar-lbl').tooltip('close');  // I need this, but it breaks the line above, causing the tooltip to flicker

}).tooltip(
{
    tooltipClass: 'bar-tooltip',
    position:
    {
        my: 'center',
        at: 'center'
    }
});

$('body').on('mouseleave', '.ui-tooltip',
function(e)
{
    $('.bar-lbl').tooltip('close');
});
jlmmns
  • 837
  • 4
  • 14
  • 25
  • Do you really need the tooltip here or would css :hover be enough? Tooltip fades away because it generates mouseout-event when you move the cursor over the generated tooltip-div. Check http://jsfiddle.net/xYS72/2/ – Esko Nov 08 '13 at 13:47
  • @Esa I'm using the tooltip, because it will display additional data which won't fit inside my element, and needs to be visible above all other elements. This tooltip content was just as an example. – jlmmns Nov 08 '13 at 13:49
  • When do you want it to close? – Esko Nov 08 '13 at 13:54
  • @Esa When hovering out of the element. – jlmmns Nov 09 '13 at 18:15
  • The last tooltip is still visible? Do you have more then one tooltip. Can you include your html? Thanks – Trevor Nov 09 '13 at 18:54
  • @Trevor I updated my post with some html. – jlmmns Nov 09 '13 at 21:06
  • @Trevor When hovering out on the `.bar-lbl` slow/normal, the last hovered tooltip stays visible. But when hovering out really fast, all tooltips are closed (desired effect). – jlmmns Nov 09 '13 at 21:13
  • Hmm I can't reproduce the issue on my computer/browser.. You could try putting a set timeout around your `tooltip close` function `setTimeout(function(){ $('.bar-lbl').tooltip('close'); },10);` that might help the close function to run everytime. – Trevor Nov 09 '13 at 22:07
  • @Trevor I updated my last piece of code, to clarify what is happening. See the `comments` next to the `on('mouseleave')` event on my `.bar-lbl`. – jlmmns Nov 10 '13 at 10:59
  • @jlmmns This works fine for me in google chrome http://jsfiddle.net/AckAj/ I looked at your update, the problem with `$('.bar-lbl').tooltip('close');` in your `.bar-lbl mouseleave function` is that as soon as you scroll over and the tooltip is created the tool tip goes in front automatically triggering the `.bar-lbl mouseleave function` which just closes it as soon as it opens.. I could try my example fiddle in the same browser you are using to see if I can see the problem.. What browser and version are you using? – Trevor Nov 11 '13 at 18:29

4 Answers4

5

Just putting the first part of your code for reference.

$('.bar-lbl').tooltip(
    {
        tooltipClass: 'bar-tooltip',
        position:
    {
        my: 'center',
        at: 'center'
    }
});

$('.bar-lbl').on('mouseleave', function(e)
{
    e.stopImmediatePropagation();

}).tooltip(
{
    tooltipClass: 'bar-tooltip',
    position:
    {
        my: 'center',
        at: 'center'
    }
});

For the last part, change the not working part to the following which closes the tooltip on mouseleave.

$('body').on('mouseleave', '.ui-tooltip',
function(e)
{
    $('.bar-lbl').tooltip('close');    
});
Trevor
  • 16,080
  • 9
  • 52
  • 83
  • I had to place a `mouseenter` event at the top, to hide other tooltips when hovering another element. (see update) – jlmmns Nov 09 '13 at 18:16
1

This is how I handled it. You add a marker class .tooltip-anchor to every element that should have a tooltip. You also add data-tooltip-content attribute which contains the selector of that anchor's tooltip content. Finally, to opt-in to hover keeping the tooltip open, you add .tooltip-is-hoverable to the anchor. The code prevents jQuery UI from closing the tooltip and instead manages its own closing timers.

HTML:

<span class="tooltip-anchor tooltip-is-hoverable"
      data-tooltip-content="#my-tooltip-content"
      >
  this has a tooltip <span>(this inner span won't have a second tooltip)</span>
</span>
<div id="my-tooltip-content">
  When your tooltips don't vanish:
  <a href="https://www.google.com/search?q=be+happy">you might feel</a>.
</div>

JS:

$(() => {
  $(".tooltip-anchor").each((index, element) => {
    let $anchor = $(element),
        contentSelector = $anchor.data("tooltip-content"),
        $content = $(contentSelector).detach();

    let timeoutId = null;
    function cancelClose() {
      if (timeoutId) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }
    }
    function scheduleClose() {
      cancelClose();
      timeoutId = setTimeout(() => {
        $anchor.tooltip("close");
        timeoutId = null;
      }, 250)
    }

    let tooltipOptions = {
      content: () => $content,
      // Only the tooltip anchor should get a tooltip.  If we used "*", every child tag would
      //  also get a tooltip.
      items: ".tooltip-anchor",
      position: {
        my: "center top+10",
        at: "center bottom",
        collision: "flipfit",
      },
    };
    if ($anchor.is(".tooltip-is-hoverable")) {
      $.extend(tooltipOptions, {
        open: (e) => {
          let $anchor = $(e.target),
              contentSelector = $anchor.data("tooltip-content"),
              $content = $(contentSelector),
              $tooltip = $content.closest("[role='tooltip'],.ui-tooltip");
          $tooltip.on('mouseenter', cancelClose),
          $tooltip.on('mouseleave', scheduleClose);
        },
        tooltipClass: "hoverable-tooltip",
      });

      // Prevent jquery UI from closing the tooltip of an anchor with a hoverable tooltip.
      $anchor.on('mouseleave', (e) => {
        // Instead, schedule a close.
        scheduleClose();
        e.stopImmediatePropagation();
      });
    }
    $anchor.tooltip(tooltipOptions);
  });
});
Carl G
  • 17,394
  • 14
  • 91
  • 115
0

Maybe my approach can help:

let delay_time = 2000;

$elem.tooltip({
    hide: {
        delay: delay_time
    },
    close: function(event, ui) {
        ui.tooltip.hover(
            function() {
                $(this).stop(true).fadeTo(400, 1);
            },
            function() {
                $(this).delay(delay_time).fadeOut(400, function(){ $(this).remove(); })
            }
        );
    }
});
sNICkerssss
  • 6,312
  • 1
  • 24
  • 16
0

Considering your updated question it seems all that's missing is a "smart" detection whether to propagate the mouseleave event or not.

Reference: Detect IF hovering over element with jQuery

Notice the change in .on('mouseleave' :

$('.bar-lbl').tooltip({
    tooltipClass: 'bar-tooltip',
    position: {
        my: 'center',
        at: 'center'
    }
})

.on('mouseenter', function(e){
    $('.bar-lbl').not($(this)).tooltip('close');    
})

.on('mouseleave', function(e){
    // only stop propagation if a tooltip is hovered
    if( $('.ui-tooltip').is(":hover") ){
        e.stopImmediatePropagation();
    }
});

$('body').on('mouseleave', '.ui-tooltip', function(e){
    $('.bar-lbl').tooltip('close');
});
nuala
  • 2,681
  • 4
  • 30
  • 50