12

After updating to bootstrap 2.3.0 I noticed strange behaviour of popover tooltip.

If you enter the field and try to navigate away, tooltip would disappear and hide input field. This issue occurs only with latest version of bootstrap, not sure what exactly was changed, but 2.2.2 works well with Prototype.

jQuery.noConflict();
jQuery('input[data-content]').popover({
    trigger: 'focus'
});

Here is working example: http://jsfiddle.net/U6EDs/4/

Nazariy
  • 6,028
  • 5
  • 37
  • 61
  • Try using this bootstrap friendly prototype.js as it fixed my issues: http://stackoverflow.com/questions/19139063/twitter-bootstrap-3-dropdown-menu-disappears-when-used-with-prototype-js – MWD Oct 03 '13 at 15:02

8 Answers8

22

This github issue https://github.com/twitter/bootstrap/issues/6921 describes what you are seeing. I am copying the information from the issue.

these lines in bootstrap-tooltip.js cause the problem:

  , hide: function () {
  var that = this
    , $tip = this.tip()
    , e = $.Event('hide')   ///this line

  this.$element.trigger(e)  /// this line
  if (e.isDefaultPrevented()) return //and this line 

PrototypeJS adds methods to the Element prototype so when jQuery tries to trigger the hide() method on an element it is actually firing the PrototypeJS hide() method, which is equivalent to the jQuery hide() method and sets the style of the element to display:none;

You have a few options

  1. remove the lines marked above in the js file
  2. rename the event to something else (not hide)
  3. use the PrototypeJS fork of BootStrap (currently works with 2.3.2)

http://github.com/jwestbrook/bootstrap-prototype

*****DISCLAIMER***

I am the developer that is currently porting the BootStrap JS components to PrototypeJS

Geek Num 88
  • 5,264
  • 2
  • 22
  • 35
  • I'm having this problem, but it seems Bootstrap has namespaced its events in v3. Any other ideas why it could still be happening? – Sam Selikoff Sep 18 '13 at 16:48
  • 1
    actually they are not using the traditional namespacing format `namespace:event` they are namespacing using the `event.namespace` which still triggers the PrototypeJS hide event – Geek Num 88 Sep 18 '13 at 17:13
  • Not to my knowledge - I'll have the v3 javascript components done soon which would fix everything for you – Geek Num 88 Sep 18 '13 at 21:16
  • 1
    No workaround for PrototypeJS? I mean, i've had problems with prototype stringidying json in the wrong way, so the fix was: "delete Array.prototype.toJSON;". No similar solution to this? – Robin Jonsson Oct 02 '13 at 06:48
  • @RobinJonsson if you really want to I suppose you could delete the `hide()` method on the Element prototype - however it is a core method that other methods depend on and that could break other scripts. Also I don't see a `toJSON()` method added to the Array prototype - are you using the most recent version (1.7.1)? – Geek Num 88 Oct 02 '13 at 14:45
  • @GeekNum88 See http://stackoverflow.com/questions/710586/json-stringify-bizarreness – Robin Jonsson Oct 03 '13 at 06:43
  • @GeekNum88 And also, you were right. The hide method is nested in Element.Methods.*. Removing it will cause other stuff to break. I solved it by making an ugly onmouseenter and onmouseleave listener. – Robin Jonsson Oct 03 '13 at 09:07
  • @RobinJonsson Yes there used to be an `Array.toJSON()` method a few versions ago - but not in the latest, and not in the github version – Geek Num 88 Oct 03 '13 at 14:48
  • @GeekNum88 Ah, my company uses Prototype JS 1.5.1.1 but we're slowly replacing it, bit by bit. – Robin Jonsson Oct 03 '13 at 22:09
  • This is a little late, but this work around fixed my issue: http://www.contrid.co.za/2015/06/fix-bootstrap-3-and-prototype-js-conflict/ – eCamK Jan 14 '16 at 23:02
5

I know this is an old question, but I just ran into this (again) and the above answers were not going to work for me. I ended up finding a solution without hacking into any libraries. The idea is to show the element after it is hidden. There is no flickering and it works nice.

jQuery(function () {
    jQuery.noConflict();
        jQuery('input[data-content]').popover({
            trigger: 'focus'
        }).on('hidden.bs.popover', function(){
            jQuery(this).show();
        });
});

You could use the same technique for tooltips, just use on('hidden.bs.tooltip'), ...

Note, I am using Bootstrap 3.0.0 for most of my personal testing. Here is an updated fiddle from the question which uses BS 2.3: http://jsfiddle.net/calypsonian/U6EDs/13/

Tim Mickey
  • 361
  • 3
  • 10
  • Awesome, you saved my day ... unfortunately the element in question briefly disappears and then shows up again; do you know of a way to fix this? – user13955 Mar 24 '14 at 23:47
  • I get the flicker too but its better than disappearing. Thanks! – Supamic Mar 17 '15 at 21:53
5

I spent some time looking into this.

Here's why it happens.

Prototype's strategy is to extend the DOM element classes with new behaviors, for example 'hide'. Equivalent of:

HTMLElement.prototype.hide = function(...) {...};

jQuery makes an assumption that if an element has a function with the same name as an event, then it is considered the default behavior of the element when that event is triggered. For example, this is useful for the events 'focus', 'blur', etc, where there is both an event by that name 'focus' and a matching function on the element .focus(). When you call jQuery's event.preventDefault(), it's not actually blocking the default behavior. Instead it's the inverse -- if you don't call event.preventDefault(), then jQuery looks for a function with the same name as the event and then calls it if it exists.

Here's the jQuery src that manually calls the "default behavior" function: https://github.com/jquery/jquery/blob/d837f119c3729565103005d5d7fa89e1dd8110cb/src/event.js#L338

Thus, we have our problem. Prototype extends the base HTMLElement prototype with new functions (namely, 'hide'). jQuery considers these functions to be default behaviors when an event of the same name is fired. Bootstrap's JS triggers an event of the same name ('hide'), which mistakenly causes jQuery to call Prototype's Element.hide() function.

I am going to look into a solution based on this information. Probably something along the lines of:

jQuery(window).load(function() {
    // Defer this code until after the window 'load' event finishes firing
    setTimeout(function() {
        window.HTMLElement &&
        window.HTMLElement.prototype.hide &&
        delete window.HTMLElement.prototype.hide;
    }, 0);
});
colllin
  • 9,442
  • 9
  • 49
  • 65
2

For me the better (without changing Bootstrap source code) is to prevent event and manually remove "in" class which hides tooltip:

jQuery("input").on('hide.bs.tooltip', function(e) {
    e.preventDefault();
    $("div.tooltip.in").removeClass('in');

});

Only issue is that it prevents from firing "hidden.bs.tooltip" event. But if you don't mind it I suggest using this solution as it doesn't cause flickering

MatiK
  • 573
  • 1
  • 7
  • 21
  • It did not work for me, i had to change the code a bit. ` jQuery(".tooltipped").on('hide.bs.tooltip', function(e) { e.preventDefault(); $("div.tooltip.in").remove(); });` – Inluxc Sep 16 '15 at 14:45
  • 1
    Excellent answer. Also works for popover (example given is using tooltip). – BlueC Apr 11 '17 at 14:58
2

You can show it again on mouseleave event like,

jQuery(function(){  
    jQuery('[data-toggle="popover"]').popover().on('mouseleave',function(){
       $(this).show(0); // show it again when mouse leave event fires
    });
});

Live Demo

Rohan Kumar
  • 40,431
  • 11
  • 76
  • 106
1

I added an important CSS style to override the element style that prototype adds. This works for me because nothing should be hiding the element.

#elemId{
  display: block!important; /* or whatever the original display was */
}
ntrrobng
  • 408
  • 5
  • 15
0

I "fixed" the bug by editing Bootstrap JS in this way:
(comment out line this.$element.trigger(e))

, hide: function () {
  var that = this
    , $tip = this.tip()
    , e = $.Event('hide')

  this.$tip.trigger(e)
//this.$element.trigger(e)
  if (e.isDefaultPrevented()) return
apaul
  • 16,092
  • 8
  • 47
  • 82
xoeoro
  • 19
  • 2
0

One possible solution is to always show the element through CSS.

[data-toggle=tooltip] {
    display: block!important;
}
Tom Franssen
  • 456
  • 4
  • 12