Popover hides parent element if used with Prototype JS

馋奶兔 提交于 2019-11-26 14:23:31

问题


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/


回答1:


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




回答2:


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/




回答3:


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);
});



回答4:


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




回答5:


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




回答6:


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 */
}



回答7:


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



回答8:


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

[data-toggle=tooltip] {
    display: block!important;
}


来源:https://stackoverflow.com/questions/15087129/popover-hides-parent-element-if-used-with-prototype-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!