How can I hold Twitter Bootstrap Popover open until my mouse moves into it?

JavascriptJqueryTwitter BootstrapTwitter Bootstrap-3Popover

Javascript Problem Overview


I have a link that uses the Twitter Bootstrap Popover version 1.3.0 to show some information. This information includes a link, but every-time I move my mouse from the link to the popover, the popover just disappears.

How can I hold popover open long enough to enable the mouse to move into it? Then when the mouse moves out of the link and popover, hide it?

Or is there some other plugin that can do this?

Javascript Solutions


Solution 1 - Javascript

With bootstrap (tested with version 2) I figured out the following code:

$("a[rel=popover]")
            .popover({
                offset: 10,
                trigger: 'manual',
                animate: false,
                html: true,
                placement: 'left',
                template: '<div class="popover" onmouseover="$(this).mouseleave(function() {$(this).hide(); });"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'

            }).click(function(e) {
                e.preventDefault() ;
            }).mouseenter(function(e) {
                $(this).popover('show');
            });

The main point is to override template with mouseleave() enabler. I hope this helps.

Solution 2 - Javascript

Bootstrap 3 and above

Simple, just use the container option and have it as the element that is calling the popover. This way, the popover is a child of the element that calls it. Hence, you are technically still hovering over the parent, because the child popover belongs to it.

For example:

HTML:

<div class="pop" data-content="Testing 12345">This has a popover</div>
<div class="pop" data-content="Testing 12345">This has a popover</div>
<div class="pop" data-content="Testing 12345">This has a popover</div>

jQuery:

Running an $.each() loop over every one of my elements that I want a popover binded to its parent. In this case, each element has the class of pop.

$('.pop').each(function () {
    var $elem = $(this);
    $elem.popover({
        placement: 'top',
        trigger: 'hover',
        html: true,
        container: $elem
    });
});

CSS:

This part is optional, but recommended. It moves the popover down by 7 pixels for easier access.

.pop .popover {
    margin-top:7px;
}

WORKING DEMO

Solution 3 - Javascript

Just to add to Marchello's example, if you want the popover to disappear if the user moves their mouse away from the popover and source link, try this out.

var timeoutObj;
$('.nav_item a').popover({
	offset: 10,
	trigger: 'manual',
	html: true,
	placement: 'right',
	template: '<div class="popover" onmouseover="clearTimeout(timeoutObj);$(this).mouseleave(function() {$(this).hide();});"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
}).mouseenter(function(e) {
	$(this).popover('show');
}).mouseleave(function(e) {
	var ref = $(this);
	timeoutObj = setTimeout(function(){
		ref.popover('hide');
    }, 50);
});

Solution 4 - Javascript

This is a little hacky, but building off of marchello's example, I did this (no need for template):

$(".trigger-link").popover({
  trigger: "manual",
}).on("click", function(e) {
  e.preventDefault();
}).on("mouseenter", function() {
  var _this = this;
  $(this).popover("show");
  $(this).siblings(".popover").on("mouseleave", function() {
    $(_this).popover('hide');
  });
}).on("mouseleave", function() {
  var _this = this;
  setTimeout(function() {
    if (!$(".popover:hover").length) {
      $(_this).popover("hide")
    }
  }, 100);
});

The setTimeout helps ensure that there's time to travel from the trigger link to the popover.

Solution 5 - Javascript

This issue on the bootstrap github repo deals with this problem. fat pointed out the experimental "in top/bottom/left/right" placement. It works, pretty well, but you have to make sure the popover trigger is not positioned statically with css. Otherwise the popover won't appear where you want it to.

HTML:

<span class="myClass" data-content="lorem ipsum content" data-original-title="pop-title">Hover me to show a popover.</span>

CSS:

/*CSS */
.myClass{ position: relative;}

JS:

$(function(){
  $('.myClass').popover({placement: 'in top'});
});  

Solution 6 - Javascript

Solution worked for us for Bootstrap 3.

var timeoutObj;
$('.list-group a').popover({
    offset: 10,
    trigger: 'manual',
    html: true,
    placement: 'right',
    template: '<div class="popover" onmouseover="$(this).mouseleave(function() {$(this).hide();});"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
}).mouseenter(function(e) {
    $(this).popover('show');
}).mouseleave(function(e) {
    var _this = this;
    setTimeout(function() {
        if (!$(".popover:hover").length) {
            $(_this).popover("hide");
        }
    }, 100);
}); 

Solution 7 - Javascript

Here's my take: http://jsfiddle.net/WojtekKruszewski/Zf3m7/22/

Sometimes while moving mouse from popover trigger to actual popover content diagonally, you hover over elements below. I wanted to handle such situations – as long as you reach popover content before the timeout fires, you're save (the popover won't disappear). It requires delay option.

This hack basically overrides Popover leave function, but calls the original (which starts timer to hide the popover). Then it attaches a one-off listener to mouseenter popover content element's.

If mouse enters the popover, the timer is cleared. Then it turns it listens to mouseleave on popover and if it's triggered, it calls the original leave function so that it could start hide timer.

var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj){
  var self = obj instanceof this.constructor ?
    obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
  var container, timeout;

  originalLeave.call(this, obj);

  if(obj.currentTarget) {
    container = $(obj.currentTarget).siblings('.popover')
    timeout = self.timeout;
    container.one('mouseenter', function(){
      //We entered the actual popover – call off the dogs
      clearTimeout(timeout);
      //Let's monitor popover content instead
      container.one('mouseleave', function(){
        $.fn.popover.Constructor.prototype.leave.call(self, self);
      });
    })
  }
};

Solution 8 - Javascript

Finally I fix this problem. Popover disappear is because Popover not child node of link, it is child node of body.

So fix it is easy, change bootstrap-twipsy.js content:

change .prependTo(document.body) to .prependTo(this.$element)

and fix position problem cause by change.

and some use link tiger popover will cause popover with link too, so add a span contain link, so problem solved.

Solution 9 - Javascript

This is a version of Wojtek Kruszewski solution. This version handle popover blink when mouse go back to trigger. http://jsfiddle.net/danielgatis/QtcpD/

(function($) {
      var originalLeave = $.fn.popover.Constructor.prototype.leave;
      $.fn.popover.Constructor.prototype.leave = function(obj) {
        var self = (obj instanceof this.constructor ? obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data("bs." + this.type));
        originalLeave.call(this, obj);
        if (obj.currentTarget) {
          var current = $(obj.currentTarget);
          var container = current.siblings(".popover");
          container.on("mouseenter", function() {
            clearTimeout(self.timeout);
          });
          container.on("mouseleave", function() {
            originalLeave.call(self, self);
          });
        }
      };
      
      var originalEnter = $.fn.popover.Constructor.prototype.enter;
      $.fn.popover.Constructor.prototype.enter = function(obj) {
        var self = (obj instanceof this.constructor ? obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data("bs." + this.type));
        clearTimeout(self.timeout);
        if (!$(obj.currentTarget).siblings(".popover:visible").length) {
          originalEnter.call(this, obj);
        }
      };
    })(jQuery);

Solution 10 - Javascript

I tried the solutions from @Wotjek Kruszewski and @danielgatis, but neither worked for me. Caveat: I'm using Bootstrap v2.1.0, not v3. This solution is in coffeescript (why are people still using plain javascript? =)).

(($) ->
  originalLeave = $.fn.popover.Constructor::leave
  $.fn.popover.Constructor::leave = (e) ->
    self = $(e.currentTarget)[@type](@_options).data(@type)
    originalLeave.call @, e

    if e.currentTarget
      container = $(".popover")
      container.one "mouseenter", ->
        clearTimeout self.timeout

        container.one "mouseleave", ->
          originalLeave.call self, e
) jQuery

Solution 11 - Javascript

Here is what i did:

e = $("a[rel=popover]")
e.popover({
    content: d, 
    html:true, 
    trigger:'hover',
    delay: {hide: 500},
    placement: 'bottom',
    container: e, 
})

This is a very simple and awesone solution to this probelm, which i found out by looking into the bootstrap tooltip code. In Bootstrap v3.0.3 here is the line of code i noticed:

this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)

this says that if container property of popover is defined then the popover gets appendTo() the element instead of insertAfter() the original element, all you need to do is just pass the element as container property. Because of appendTo() the popover becomes part of the link on which the hover event was binded and thus keeps the popover open when mouse moves on it.

Solution 12 - Javascript

This works for me on BootStrap 3:

el.popover({
  delay: {hide: 100}
}).on("shown.bs.popover", function(){
  el.data("bs.popover").tip().off("mouseleave").on("mouseleave", function(){
    setTimeout(function(){
      el.popover("hide");
    }, 100);
  });
}).on("hide.bs.popover", function(ev){
  if(el.data("bs.popover").tip().is(":hover"))
    ev.preventDefault();
});

Solution 13 - Javascript

At the end of the conversation linked by @stevendaniels is a link to a Twitter Bootstrap extension called BootstrapX - clickover by Lee Carmichael. This changes the popover from an overlarge tooltip into an interactive control, which can be closed by clicking elsewhere on the form, a close button, or after a timeout. Its easy to use, and worked very well for the project I needed it in. Some examples of its usage can be found here.

Solution 14 - Javascript

I didn't like any of the answers I've found, so I combined some answers that were close to make the following code. It allows you to end up just typing $(selector).pinnablepopover(options); every time you want to make a 'pinnable' popover.

Code that makes things easy:

$.fn.popoverHoverShow = function ()
{
    if(this.data('state') !== 'pinned')
    {
        if(!this.data('bs.popover').$tip || (this.data('bs.popover').$tip && this.data('bs.popover').$tip.is(':hidden')))
        {
            this.popover('show');
        }
    }
};
$.fn.popoverHoverHide = function ()
{
    if (this.data('state') !== 'pinned')
    {
        var ref = this;
        this.data('bs.popover').$tip.data('timeout', setTimeout(function(){ ref.popover('hide') }, 100))
        .on('mouseenter', function(){ clearTimeout($(this).data('timeout')) })
        .on('mouseleave', function(){ $(this).data('timeout', setTimeout(function(){ ref.popover('hide') }, 100)) });
        this.on('mouseenter', function(){ clearTimeout($(this).data('timeout')) });
    }
};
$.fn.popoverClickToggle = function ()
{
    if (this.data('state') !== 'pinned')
    {
        this.data('state', 'pinned');
    }
    else
    {
        this.data('state', 'hover')
    }
};
$.fn.pinnablepopover = function (options)
{
    options.trigger = manual;
    this.popover(options)
    .on('mouseenter', function(){ $(this).popoverHoverShow() })
    .on('mouseleave', function(){ $(this).popoverHoverHide() })
    .on('click', function(){ $(this).popoverClickToggle() });
};

Example usage:

$('[data-toggle=popover]').pinnablepopover({html: true, container: 'body'});

Solution 15 - Javascript

After seeing all Answer I made this I think it will be helpful .You Can manage Everything which you need. Many answer doesn't make show delay I use this. Its work very nice in my project
/****** /*************************************************************/

<div class='thumbnail' data-original-title=''  style='width:50%'>    
 <div id='item_details' class='popper-content hide'>
    <div>
        <div style='height:10px'> </div>
        <div class='title'>Bad blood </div>
        <div class='catagory'>Music </div>
    </div>
    
  </div>
  HELLO POPOVER
</div>"

/****************SCRIPT CODE ****************** PLEASE USE FROM HEAR ******/

$(".thumbnail").popover({
trigger: "manual" ,
html: true,
animation:true,
container: 'body',
placement: 'auto right',
content: function () {
    return $(this).children('.popper-content').html();
}}) .on("mouseenter", function () {
var _this = this;

$('.thumbnail').each(function () {
    $(this).popover('hide');
});
setTimeout(function(){
    if ($(_this).is(':hover')) {
        $(_this).popover("show");
    }
},1000);
$(".popover").on("mouseleave", function () {
    $('.thumbnail').each(function () {
        $(this).popover('hide');
    });
    $(_this).popover('hide');
 }); }).on("mouseleave", function () {
    var _this = this;
    setTimeout(function () {
        if (!$(".popover:hover").length) {
            $(_this).popover("hide");
        }
    }, 100); });

Solution 16 - Javascript

Now I just switch to webuiPopover, it just works.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTinyfoolView Question on Stackoverflow
Solution 1 - JavascriptmarchelloView Answer on Stackoverflow
Solution 2 - JavascriptFizzixView Answer on Stackoverflow
Solution 3 - JavascriptKevin LawrenceView Answer on Stackoverflow
Solution 4 - JavascriptclemView Answer on Stackoverflow
Solution 5 - JavascriptstevendanielsView Answer on Stackoverflow
Solution 6 - JavascriptOpcratView Answer on Stackoverflow
Solution 7 - JavascriptWojtek KruszewskiView Answer on Stackoverflow
Solution 8 - JavascriptTinyfoolView Answer on Stackoverflow
Solution 9 - JavascriptdanielgatisView Answer on Stackoverflow
Solution 10 - JavascriptMicah WinkelspechtView Answer on Stackoverflow
Solution 11 - JavascriptvinitView Answer on Stackoverflow
Solution 12 - JavascriptShogunPandaView Answer on Stackoverflow
Solution 13 - JavascriptMiika L.View Answer on Stackoverflow
Solution 14 - JavascriptTimothy ZornView Answer on Stackoverflow
Solution 15 - JavascriptAshaduzaman BhuiyanView Answer on Stackoverflow
Solution 16 - JavascriptTinyfoolView Answer on Stackoverflow