Twitter bootstrap 2.3.2 popover stay open while hovering
Asked Answered
W

2

18

I have a bottom-oriented popover that I'd like to be a bit more forgiving than the default popover, which vanishes as soon as the mouse leaves the trigger.

$('#example').popover({
  html: true,
  trigger: 'hover',
  container: '#example',
  placement: 'bottom',
  content: function () {
      return '<div class="box">here is some content</div>';
  }
});

I've got it to stay open as long as the mouse is over the trigger or the popover content, but that's tough for the user, since they've got to mouse from the trigger element to the arrow to the content without leaving those areas in order to interact with the popover. Two solutions in mind, neither is working:

1) the delay option ought to do this. adding delay: {hide: 500} to the popover call leaves the popover open after the mouse leaves, but re-entering the trigger elem or the popover before it disappears does not tell bootstrap to keep the popover open, so goes away at the end of the initial timeout.

2) widen the arrow's containing element so that the mouse going from trigger element to background between trigger element and popover to popover works (the mouse then would never have left the trigger/element). The following works except the arrow is drawn with overlapping CSS borders, so the background is not transparent: http://jsfiddle.net/HAZS8/

.popover.bottom .arrow {
    left: 0%;
    padding-left:50%;
    padding-right:50%;
}

The workaround is to hard-wire the mouseover and mouseleave events with jquery, or to replace the overlapping-borders arrow with an image. Better fixes?

Worsted answered 24/5, 2013 at 17:2 Comment(0)
A
19

You can handle the show and hide events for the popover:

$('#example').popover({
    html: true,
    trigger: 'hover',
    container: '#example',
    placement: 'bottom',
    content: function () {
        return '<div class="box">here is some content</div>';
    },
    animation: false
}).on({
    show: function (e) {
        var $this = $(this);

        // Currently hovering popover
        $this.data("hoveringPopover", true);

        // If it's still waiting to determine if it can be hovered, don't allow other handlers
        if ($this.data("waitingForPopoverTO")) {
            e.stopImmediatePropagation();
        }
    },
    hide: function (e) {
        var $this = $(this);

        // If timeout was reached, allow hide to occur
        if ($this.data("forceHidePopover")) {
            $this.data("forceHidePopover", false);
            return true;
        }

        // Prevent other `hide` handlers from executing
        e.stopImmediatePropagation();

        // Reset timeout checker
        clearTimeout($this.data("popoverTO"));

        // No longer hovering popover
        $this.data("hoveringPopover", false);

        // Flag for `show` event
        $this.data("waitingForPopoverTO", true);

        // In 1500ms, check to see if the popover is still not being hovered
        $this.data("popoverTO", setTimeout(function () {
            // If not being hovered, force the hide
            if (!$this.data("hoveringPopover")) {
                $this.data("forceHidePopover", true);
                $this.data("waitingForPopoverTO", false);
                $this.popover("hide");
            }
        }, 1500));

        // Stop default behavior
        return false;
    }
});

DEMO: http://jsfiddle.net/L4Hc2/

It doesn't seem like there's anything built-in for the popover for the functionality you want, so this is what I came up with :)

What's nice is that it only allows handlers to execute if they really should - if the popover is actually being hidden or actually being shown. Also, each instance of a popover is unique from each other, so there is no global trickery going on.

Artillery answered 24/5, 2013 at 18:29 Comment(4)
Thanks! I couldn't find anything build-in for the twitter popover or for the jqueryUI one. This works well though.Worsted
@Worsted Me either, so I just tried something custom and it seemed to work :)Artillery
This answer doesn't work for me, and your jsfiddle doesn't seem functional in either browsers I have checked (recent firefox or chrome)Hereafter
@Hereafter It would've been more helpful if you looked at your browser console or something. The URLs for the bootstrap library files were broken. I simply replaced them and it's back to normal. You can check the updated fiddleArtillery
H
32

I have a more generic approach to solving this one, which I'm using myself. It involves overloading the popover's hide function, checking if the associated tooltip is being hovered over, and reacts appropriately - rather than adding all that event handling & html5 data setting.

(function($) {

    var oldHide = $.fn.popover.Constructor.prototype.hide;

    $.fn.popover.Constructor.prototype.hide = function() {
        if (this.options.trigger === "hover" && this.tip().is(":hover")) {
            var that = this;
            // try again after what would have been the delay
            setTimeout(function() {
                return that.hide.call(that, arguments);
            }, that.options.delay.hide);
            return;
        }
        oldHide.call(this, arguments);
    };

})(jQuery);

Load this after your bootstrap & jQuery sources.

Hereafter answered 6/11, 2013 at 5:32 Comment(6)
do you have a jsfiddle for a demo?Nitrosamine
hello there, how can I attach a php script inside the content, like for example I want to add a wordpress script <?php wp_nav_menu() ?> in the contentAlkene
Shouldn't you be using apply instead of call in both instances, since you're passing it an array(ish)?Aenea
Also, arguments inside the setTimeout callback will refer to the arguments passed by setTimeout to that callback, when I think you actually want the arguments passed to hide, in which case you should do e.g. args = arguments at the same time you do that = this, and then use args in the setTimeout callback.Aenea
One minor point -- the current version of bootstrap supports passing a space-delimited list of triggers in popover options. If you pass trigger: 'hover focus', for example, this sample won't work. Simply replacing this.options.trigger === with this.options.trigger.indexOf should fix it.Agenda
This is great. A note though: You have to set delay hide option (e.g. hide: 1000) to make it work. Otherwise, it stays only if your mouse is go through the triangle tip.Piles
A
19

You can handle the show and hide events for the popover:

$('#example').popover({
    html: true,
    trigger: 'hover',
    container: '#example',
    placement: 'bottom',
    content: function () {
        return '<div class="box">here is some content</div>';
    },
    animation: false
}).on({
    show: function (e) {
        var $this = $(this);

        // Currently hovering popover
        $this.data("hoveringPopover", true);

        // If it's still waiting to determine if it can be hovered, don't allow other handlers
        if ($this.data("waitingForPopoverTO")) {
            e.stopImmediatePropagation();
        }
    },
    hide: function (e) {
        var $this = $(this);

        // If timeout was reached, allow hide to occur
        if ($this.data("forceHidePopover")) {
            $this.data("forceHidePopover", false);
            return true;
        }

        // Prevent other `hide` handlers from executing
        e.stopImmediatePropagation();

        // Reset timeout checker
        clearTimeout($this.data("popoverTO"));

        // No longer hovering popover
        $this.data("hoveringPopover", false);

        // Flag for `show` event
        $this.data("waitingForPopoverTO", true);

        // In 1500ms, check to see if the popover is still not being hovered
        $this.data("popoverTO", setTimeout(function () {
            // If not being hovered, force the hide
            if (!$this.data("hoveringPopover")) {
                $this.data("forceHidePopover", true);
                $this.data("waitingForPopoverTO", false);
                $this.popover("hide");
            }
        }, 1500));

        // Stop default behavior
        return false;
    }
});

DEMO: http://jsfiddle.net/L4Hc2/

It doesn't seem like there's anything built-in for the popover for the functionality you want, so this is what I came up with :)

What's nice is that it only allows handlers to execute if they really should - if the popover is actually being hidden or actually being shown. Also, each instance of a popover is unique from each other, so there is no global trickery going on.

Artillery answered 24/5, 2013 at 18:29 Comment(4)
Thanks! I couldn't find anything build-in for the twitter popover or for the jqueryUI one. This works well though.Worsted
@Worsted Me either, so I just tried something custom and it seemed to work :)Artillery
This answer doesn't work for me, and your jsfiddle doesn't seem functional in either browsers I have checked (recent firefox or chrome)Hereafter
@Hereafter It would've been more helpful if you looked at your browser console or something. The URLs for the bootstrap library files were broken. I simply replaced them and it's back to normal. You can check the updated fiddleArtillery

© 2022 - 2024 — McMap. All rights reserved.