Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Popover hides parent element if used with Prototype JS

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/

like image 408
Nazariy Avatar asked Feb 26 '13 10:02

Nazariy


4 Answers

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

like image 116
Geek Num 88 Avatar answered Nov 14 '22 05:11

Geek Num 88


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/

like image 29
Tim Mickey Avatar answered Nov 14 '22 03:11

Tim Mickey


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);
});
like image 5
colllin Avatar answered Nov 14 '22 03:11

colllin


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

like image 2
MatiK Avatar answered Nov 14 '22 04:11

MatiK