Twitter Bootstrap 3下拉菜单与prototype.js一起使用时会消失

41

在Magento网站中使用Bootstrap 3和Prototype.js存在问题。

基本上,如果您单击下拉菜单(Our Products),然后单击背景,下拉菜单(Our Products)会消失(prototype.js将“display:none;”添加到li中)。

此处演示了该问题: http://ridge.mydevelopmentserver.com/contact.html

您可以在下面的链接中查看不包括prototype.js在内的页面上的下拉菜单正常工作: http://ridge.mydevelopmentserver.com/

是否有其他人遇到过此问题或对冲突可能有解决方案?

简易解决方法:

只需用此与Bootstrap兼容的原型文件替换Magento的prototype.js文件即可:

https://raw.github.com/zikula/core/079df47e7c1f536a0d9eea2993ae19768e1f0554/src/javascript/ajax/original_uncompressed/prototype.js

您可以在此处查看所做更改以解决Bootstrap问题的原型文件:

https://github.com/zikula/core/commit/079df47e7c1f536a0d9eea2993ae19768e1f0554

注意:在包括prototype.js之前,必须将JQuery包含在您的Magento皮肤中。例如:

<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/prototype/prototype.js"></script>
<script type="text/javascript" src="/js/lib/ccard.js"></script>
<script type="text/javascript" src="/js/prototype/validation.js"></script>
<script type="text/javascript" src="/js/scriptaculous/builder.js"></script>
<script type="text/javascript" src="/js/scriptaculous/effects.js"></script>
<script type="text/javascript" src="/js/scriptaculous/dragdrop.js"></script>
<script type="text/javascript" src="/js/scriptaculous/controls.js"></script>
<script type="text/javascript" src="/js/scriptaculous/slider.js"></script>
<script type="text/javascript" src="/js/varien/js.js"></script>
<script type="text/javascript" src="/js/varien/form.js"></script>
<script type="text/javascript" src="/js/varien/menu.js"></script>
<script type="text/javascript" src="/js/mage/translate.js"></script>
<script type="text/javascript" src="/js/mage/cookies.js"></script>
<script type="text/javascript" src="/js/mage/captcha.js"></script>

2
请看我对类似问题的回答:https://dev59.com/umUp5IYBdhLWcg3wvZVV#15095654 - Geek Num 88
1
找了两天才找到这篇文章和Bootstrap友好版本的原型。谢谢!你知道有什么缺点或兼容性问题吗?我注意到我必须先加载jQuery并停止使用noConflict。 - Dihedral
1
谢谢。花了好几个小时才解决这个问题。jQuery 必须在 prototype 之前引入,好的提示。 - Ashley Swatton
这解决了下拉菜单问题,但在Magento后台,它破坏了“添加属性”功能。 - triston
谢谢,我会收藏这个问题以备参考。 - HungryDB
显示剩余2条评论
8个回答

43

我还使用了这里的代码:http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework ,但不需要修改任何源代码。只需在原型和jQuery包含后面的某个位置放置以下代码:

(function() {
    var isBootstrapEvent = false;
    if (window.jQuery) {
        var all = jQuery('*');
        jQuery.each(['hide.bs.dropdown', 
            'hide.bs.collapse', 
            'hide.bs.modal', 
            'hide.bs.tooltip',
            'hide.bs.popover',
            'hide.bs.tab'], function(index, eventName) {
            all.on(eventName, function( event ) {
                isBootstrapEvent = true;
            });
        });
    }
    var originalHide = Element.hide;
    Element.addMethods({
        hide: function(element) {
            if(isBootstrapEvent) {
                isBootstrapEvent = false;
                return element;
            }
            return originalHide(element);
        }
    });
})();

1
非常感谢您提供的解决方案。除了 hide() 之外,还有哪些事件可能会产生冲突? - Anthony
all.on..... 行出现了错误,错误信息为 TypeError: all.on 不是一个函数 - Janith Chinthana
@Anthony 我目前还没有找到,你有吗? - evgeny.myasishchev
@evgeny.myasishchev,谢谢,这段代码对我有效。但是当我点击菜单时,它没有重定向到特定的页面。 - Ananth
谢谢Evgeny,这个解决方案对我很有效!你让我的一天变得美好了! - Debakant Mohanty
太棒了!我们公司使用基于Bootstrap的自定义框架,现在开始将其移植到Magento中。当下拉菜单、选项卡或模态框无法正常工作时,我已经抓狂了好几天。非常感谢你! - Thomas Harding

8
晚来的宾客,但找到了此github问题,其中链接到这个信息页面,该页面链接到这个jsfiddle,非常好用。它不会在每个jQuery选择器上打补丁,我认为这是迄今为止最好的解决方法。将代码复制到此处以帮助未来的人们:
jQuery.noConflict();
if (Prototype.BrowserFeatures.ElementExtensions) {
  var pluginsToDisable = ['collapse', 'dropdown', 'modal', 'tooltip', 'popover'];
  var disablePrototypeJS = function (method, pluginsToDisable) {
    var handler = function (event) {
      event.target[method] = undefined;
      setTimeout(function () {
        delete event.target[method];
      }, 0);
    };
    pluginsToDisable.each(function (plugin) {
      jQuery(window).on(method + '.bs.' + plugin, handler);
    });
  };


  disablePrototypeJS('show', pluginsToDisable);
  disablePrototypeJS('hide', pluginsToDisable);
}

这是最佳解决方案。非常容易理解它在做什么,并且如果您已经使用jquery,则可以将其放入现有的无冲突js文件中。 - Justin

7

如果您不想运行额外的脚本,可以添加一个简单的CSS覆盖来防止它被隐藏。虽然晚了一步,但仍然有用。

.dropdown {
    display: inherit !important;
}

通常在CSS中使用!important是不被建议的,但我认为在这种情况下这是可以接受的。


如果这与 Prototype 的预期功能冲突,请使用 .nav .dropdown 优化选择器。 - Chris Watts
非常感谢,真是太棒了,先生!非常感谢! - Jeff Yates

7

不建议使用jQuery的*选择器。这会选取页面上的所有DOM对象并将其放入变量中。 我建议选择使用Bootstrap特定组件的元素。以下解决方案仅使用下拉菜单组件:

(function() {
    var isBootstrapEvent = false;
    if (window.jQuery) {
        var all = jQuery('.dropdown');
        jQuery.each(['hide.bs.dropdown'], function(index, eventName) {
            all.on(eventName, function( event ) {
                isBootstrapEvent = true;
            });
        });
    }
    var originalHide = Element.hide;
    Element.addMethods({
        hide: function(element) {
            if(isBootstrapEvent) {
                isBootstrapEvent = false;
                return element;
            }
            return originalHide(element);
        }
    });
})();

我喜欢不使用“*”。但是,这个问题会影响其他 Twitter Bootstrap 功能,比如弹出框。因此,需要选择多个元素,尽管不是全部。 - Phil

2

2
使用MWD建议的与Bootstrap兼容的prototype.js文件替换Magento的文件时,出现了错误,导致无法保存可配置产品。
Uncaught TypeError: Object [object Array] has no method 'gsub' prototype.js:5826

(运行Magento 1.7.0.2版本)

evgeny.myasishchev的解决方案非常好。

(function() {
    var isBootstrapEvent = false;
    if (window.jQuery) {
        var all = jQuery('*');
        jQuery.each(['hide.bs.dropdown', 
            'hide.bs.collapse', 
            'hide.bs.modal', 
            'hide.bs.tooltip'], function(index, eventName) {
            all.on(eventName, function( event ) {
                isBootstrapEvent = true;
            });
        });
    }
    var originalHide = Element.hide;
    Element.addMethods({
        hide: function(element) {
            if(isBootstrapEvent) {
                isBootstrapEvent = false;
                return element;
            }
            return originalHide(element);
        }
    });
})();

0

这个答案帮助我解决了bootstrapprototype冲突的问题。

正如@GeekNum88所描述的那样,

PrototypeJS向元素原型添加方法,因此当jQuery尝试在元素上触发hide()方法时,实际上是触发了PrototypeJS的hide()方法,它等同于jQuery的hide()方法,并将元素的样式设置为display:none;

正如您在问题本身中建议的那样,您可以使用bootstrap友好的prototype,或者您可以简单地注释掉bootstrap中的几行代码,如下所示:

Tooltip.prototype.hide函数内部

this.$element.trigger(e)
if (e.isDefaultPrevented()) return

-1

我意识到这篇文章已经相当老了,但是似乎没有人提到的答案是简单地“修改jQuery”。您只需要在jQuery的触发函数中添加几个额外的检查(可以在大约332行左右找到)即可。

扩展这个if语句:

// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {

...说这个:

// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
// Check for global Element variable (PrototypeJS) and ensure we're not triggering one of its methods.
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) &&
        ( !window.Element || !jQuery.isFunction( window.Element[ type ] ) ) ) {

如果你正在使用已编译(完整的)jQuery库,那么"#6170"只会在jQuery中提到一次,因此你可以快速检查该字符串。


修改库总是不好的建议。如果您不修改新版本,就无法更新jQuery。如果您忘记更新它,事情会出错。您的修改可能会破坏比您修复的更多的东西。 - medoingthings
最高评分答案正在替换PrototypeJS的功能。我不认为修改jQuery比这更危险或更糟糕。另外,我无法记住上次更新库的时间,而没有进行严格的检查和简单的单元测试将会显示缺失的修改。 - James Long
事实上,最高评分的答案是更改prototype.js。虽然这仍然不理想,但更改范围紧密限定在Bootstrap问题上(如果您查看答案中提到的差异)。您的方法会全局更改jQuery功能,比@MWD的方法更容易出错。顺便说一句:据我所知,@CJxD的答案似乎是最优雅的,因为它不会向每个单独的DOM元素添加JavaScript事件。再次强调,最高评分的答案可能不是每个人都应该选择的最佳答案。简而言之:更改库时要小心。 - medoingthings

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接