41

I have an issue when using bootstrap 3 & prototype.js together on a magento website.

Basically if you click on the dropdown menu (Our Products) & then click on the background, the dropdown menu (Our Products) disappears (prototype.js adds "display: none;" to the li).

Here is a demo of the issue: http://ridge.mydevelopmentserver.com/contact.html

You can see that the dropdown menu works like it should without including prototype.js on the page at the link below: http://ridge.mydevelopmentserver.com/

Has anyone else ran into this issue before or have a possible solution for the conflict?

EASY FIX:

Just replace Magento's prototype.js file with this bootstrap friendly one:

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

You can see the changes made in the prototype.js file to fix the bootstrap issue here:

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

NOTE: JQuery must be include in your magento skin before prototype.js.. Example:

<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>
MWD
  • 1,632
  • 2
  • 18
  • 39
  • 2
    Take a look at my answer to a similar question http://stackoverflow.com/a/15095654/341491 – Geek Num 88 Oct 02 '13 at 14:46
  • 1
    Took 2 days before I found this post and the Bootstrap friendly version of prototype. Thank you! Know of any down-sides or compatibility issues? I noticed I had to load jQuery first and stop using noConflict. – Dihedral Oct 23 '13 at 14:43
  • 1
    Thanks. Spent hours trying to solve this. jQuery must be included before prototype, good tip. – Ashley Swatton Nov 03 '13 at 15:37
  • this fixed the dropdown issue, but in the magento backend, it broke the "Add Attribute" feature. – triston Jul 28 '15 at 16:08
  • Used the "Easy Fix" solution in custom theme, worked well but yet to test other prototype functions like checkout pages etc – BENN1TH Mar 20 '16 at 00:39
  • Thanks, will bookmark this question for reference. – HungryDB Apr 16 '16 at 06:02

8 Answers8

43

I've also used code from here: http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework but without a need to modify any source. Just put code below somewhere after prototype and jquery includes:

(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);
        }
    });
})();
Mark Moran
  • 13
  • 3
evgeny.myasishchev
  • 4,141
  • 1
  • 20
  • 15
8

Late to the party, but found this github issue which links to this informational page which links to this jsfiddle which works really nicely. It doesn't patch on every jQuery selector and is, I think, the nicest fix by far. Copying the code here to help future peoples:

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);
}
patrickdavey
  • 1,966
  • 2
  • 18
  • 25
  • This is the best solution. Very easy to understand what it is doing and makes sense to go into your existing no-conflict js file if you're already using jquery. – Justin Oct 07 '15 at 22:07
7

Using the * selector with jQuery is not advised. This takes every DOM object on the page and puts it in the variable. I would advice to select the elements that use a Bootstrap component specific. Solution below only uses the dropdown component:

(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);
        }
    });
})();
Tom Franssen
  • 456
  • 4
  • 12
  • I like not using '*'. On the flip side, this issue affects other twitter bootstrap functionality like popover. So, multiple elements have to be selected; albeit not all. – Phil Jul 14 '15 at 22:06
7

Very late to the party: if you don't feel like having extra scripts running, you can add a simple CSS override to prevent it from getting hidden.

.dropdown {
    display: inherit !important;
}

Generally the use of !important in CSS is advised against, but I think this counts as an acceptable use in my opinion.

Chris Watts
  • 6,197
  • 7
  • 49
  • 98
2

see http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework/.

It's quite an easy fix to validate the namespace of the element clicked.

Add a validation function to prototype.js:

and after that, validate the namespace before the element will be hidden:

  hide: function(element) {
    element = $(element);
    if(!isBootstrapEvent)
    {
        element.style.display = 'none';
    }
    return element;
  },
klma
  • 21
  • 3
2

Replacing Magento's prototype.js file with the bootstrap friendly version suggested by MWD is throwing an error that prevents saving configurable products:

Uncaught TypeError: Object [object Array] has no method 'gsub' prototype.js:5826

(Running Magento Magento 1.7.0.2)

evgeny.myasishchev solution worked great.

(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);
        }
    });
})();
Perez Jr
  • 21
  • 1
0

This answer helped me to get rid of bootstrap and prototype conflict issue.

As @GeekNum88 describe the matter,

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;

As you suggest in the question itself either you can use bootstrap friendly prototype or else you can simply comment out few lines in bootstrap as bellow,

inside the Tooltip.prototype.hide function

this.$element.trigger(e)
if (e.isDefaultPrevented()) return
Community
  • 1
  • 1
Janith Chinthana
  • 3,792
  • 2
  • 27
  • 54
-1

I realise that this is a pretty old post by now, but an answer that no-one else seems to have mentioned is simply "modify jQuery". You just need a couple of extra checks in jQuery's trigger function which can be found around line 332.

Extend this if statement:

// 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 ) ) {

... to say this:

// 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 ] ) ) ) {

"#6170" only seems to be mentioned in jQuery once so you can do a quick check for that string if you're working with a compiled (complete) jQuery library.

James Long
  • 4,629
  • 1
  • 20
  • 30
  • Modifying a library is always bad advice. You won’t be able to update jQuery without modifying the new version. If you forget to update it, things will break. Your modifications might break even more things than you fixed. – medoingthings Oct 19 '15 at 12:44
  • The highest-rated answer is replacing functionality from PrototypeJS. I don't see how modifying jQuery is any more dangerous or any worse advice. As an aside, I can't remember the last time I updated a library without a serious overhaul and a simple unit test would show-up the missing modification. – James Long Oct 19 '15 at 13:00
  • Indeed, the highest rated answer is changing prototype.js. While this is still not ideal, the changes are tightly scoped to the Bootstrap issue (if you look at the diff mentioned within the answer). Your approach changes jQuery functionality globally, which is much more error prone than the approach from @MWD. Btw: as far as I can tell, @CJxD’s answer seems to be the most elegant one, since it doesn’t add JavaScript events to every single DOM element. Once again the highest rated answer might not be the best answer that everyone else should go for. TLDR: Be careful with changing libraries. – medoingthings Nov 21 '15 at 11:55