Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foundation, jQuery and Prototype.JS: Dropdown & DropdownMenu not working as intended #8221

Closed
michaelscheel opened this issue Feb 23, 2016 · 20 comments

Comments

@michaelscheel
Copy link

I'm trying to use jQuery and prototype.js, combined with foundation-sites 6 (tried 6.1.2, 6.2.0-rc.1). What has work with foundation 5 ever since leads to strange results now. The first interaction with the dropdown is working fine, but further interactions increase the offset of the dropdown pane infinitely.

I could reproduce that behaviour with dropdownMenu, too. In this case the dropdown menu is hidden (display:none) after the first interaction.

I have these components imported:

foundation.core.js
foundation.util.keyboard.js
foundation.util.triggers.js
foundation.util.mediaQuery.js
foundation.util.box.js
foundation.dropdown.js

Example: https://jsfiddle.net/4pqzmr7k/1/

Here is the callstack which shows the road to the inwards of prototype.js:

$ (prototype.js:2048)
show (prototype.js:2168)
_methodized (prototype.js:456)
jQuery.extend.trigger (jquery.js:7825) ==> elemtype;
(anonymous function) (jquery.js:7875)
jQuery.extend.each (jquery.js:360)
jQuery.fn.jQuery.each (jquery.js:137)
jQuery.fn.extend.trigger (jquery.js:7874)
Dropdown.open (foundation.dropdown.js:333)
(anonymous function) (foundation.dropdown.js:217)

included prototype.js resource: https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js
included jQuery resource https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.js

@michaelscheel
Copy link
Author

Renaming the events (example: show.zf.dropdown => reveal.zf.dropdown) would solve the problem, event it doesn't feel like the right thing to do. Would that be a valid solution? If yes I would check all components, rename the events and make a pull request.

@kball
Copy link
Contributor

kball commented Apr 27, 2016

I don't think we're going to rename all of the events... This kind of interaction with prototype.js is relatively low priority for us, but this is odd, and I suspect may be related to other dropdown positioning issues. I'll take this, import the test case, and see what I can figure out.

@kball kball self-assigned this Apr 27, 2016
@sickdaflip
Copy link

+1

@Barrakkuda
Copy link

I also encountered this problem. I'm implementing Foundation 6 in a Magento 1.14 site... Magento uses Prototype.js. The drilldown menu does not work as expected because Prototype.js picks up on a Foundation "hide" event that is triggered when closing a sub menu item.

@dimstar
Copy link

dimstar commented Mar 22, 2017

The only way around this was to rename hide.zf.[whatever obj] to hideMe.zf.[whatever obj]. I know this isn't a best solution, but it works.

I am on magento 2.1.5 (which doesn't use prototype anymore, yay!) but I have to deal with a legacy extension which still does dip into the legacy-build.js (prototype.js).

@DanielRuf
Copy link
Contributor

Closing because of

I don't think we're going to rename all of the events... This kind of interaction with prototype.js is relatively low priority for us, but this is odd, and I suspect may be related to other dropdown positioning issues. I'll take this, import the test case, and see what I can figure out.

Mixing multiple frameworks will always break things.

@rbrlortie
Copy link

rbrlortie commented Feb 28, 2019

Having the same issue today. We are working on the responsive styles of our Magento 1 website and I have upgraded to the latest version of Foundation. Foundation.Dropdown does not work correctly with that mix of technologies (Magento 1 uses Prototype.js).

This can be fixed using this solution which remove listeners from Prototype:

<script>
/* jQuery no conflict */
jQuery.noConflict();

/* Workaround Foundation / Prototype conflicts */
if (Prototype.BrowserFeatures.ElementExtensions) {
    var disablePlugins = ['dropdown', 'tooltip', 'drilldown', 'dropdownmenu'];
    var preventPrototypeJS = function (method, disablePlugins) {
        var handler = function (event) {
            event.target[method] = undefined;
            setTimeout(function () {
                delete event.target[method];
            }, 0);
            console.log(method + ' - ' + disablePlugins);
        };
        disablePlugins.each(function (plugin) {
            jQuery(window).on(method + '.zf.' + plugin, handler);
        });
    };
    preventPrototypeJS('show', disablePlugins);
    preventPrototypeJS('hide', disablePlugins);
}
/* */
jQuery(function ($) {
    /* Foundation sites */
    $(document).foundation();
});
</script>

https://stackoverflow.com/questions/35602702/foundation-jquery-and-prototype-js-dropdown-dropdownmenu-not-working-as-inte

I do agree with some of the above posters that prefixing the events with foundation: or zf would prevent a lot of future headaches.

@DanielRuf
Copy link
Contributor

DanielRuf commented Feb 28, 2019

I do agree with some of the above posters that prefixing the events with foundation: would prevent a lot of future headaches.

I do not fully understand. Afaik we have .zf..

@rbrlortie
Copy link

@DanielRuf

I'm not quite sure on the details of what Prototype does behind the scene. I do know that it adds a bunch of methods to all DOM objects and this list contains hide and show.

When something triggers the default hide event, both are going to get triggered.

See: https://jsfiddle.net/qs6oh1kb/

@DanielRuf
Copy link
Contributor

In general this is an issue on the event triggering and filtering in jQuery and other frameworks.

jQuery does the following:
hide and hide.something are catched with hide.something.
The part behind the first dot is the namespace. Namespaced events are described at http://api.jquery.com/on/#event-names

So we have to filter in .on(event) also by namespace.
But the actual event.namespace is not zf.plugin, it is plugin.zf.

If we trigger hide.zf.dropdown.abc too and listen for it, it will be executed on hide.zf.dropdown.abc, hide.zf.dropdown, hide.zf and hide.

See https://codepen.io/DanielRuf/pen/xBZLXM?editors=0010

@rbrlortie
Copy link

rbrlortie commented Mar 1, 2019

You are preaching to the choirs. I'm not defending Prototype at all, I absolutely loathe it. However, it is so deeply rooted in Magento 1's front-end that removing it would require rewriting most of its core. Most projects simply eat up the slight performance loss and add jQuery and some responsive framework over it. I've worked with a lot of Magento 1 projects that were using Foundation and it's sad that the latest version does not fit well in the puzzle anymore.

If we trigger hide.zf.dropdown.abc too and listen for it, it will be executed on hide.zf.dropdown.abc, hide.zf.dropdown, hide.zf and hide

Indeed, hence my original suggestion of prefixing with foundation: or perhaps zf:. So zf:hide.dropdown would only trigger hide events as opposed to zf.hide.dropdown which would get triggered whenever someone triggers a zf event. Apologies if that jsfiddle was confusing, it did not contain my actual suggestion.

@DanielRuf
Copy link
Contributor

Indeed, hence my original suggestion of prefixing with foundation: or perhaps zf:.

This is not recommended. Namespaced events are eventname.namespace. Not prefixed but suffixed afaik.

We will not change them as this would be a breaking change. You can maybe apply a patch file.

@DanielRuf
Copy link
Contributor

Our events are hide.zf.plugin, not zf.hide.plugin ;-)

@rbrlortie
Copy link

One solution then would be to avoid commonly used names in Foundation's custom events.

The helper to close the dropdown is $('#dropdown').foundation('close'); which fires the hide.zf.dropdown event. What about close.zf.dropdown instead?

That would avoid any possible risk of collision with other plugins or libraries which does not limit their listeners to their own namespaces.

All of this being said, the above fix that I had linked resolves the problem with this particular mix of technologies (Prototype + jQuery & Foundation 6).

@DanielRuf
Copy link
Contributor

DanielRuf commented Mar 1, 2019

We have multiple and different event names: close, closed, hide, ...

All of them mean different things.

They only thing that we forgot was to filter the events by the namespace in .on.

@DanielRuf
Copy link
Contributor

close.zf.dropdown

.on('close.zf.dropdown ') is called on close, close.zf and close.zf.dropdown so this is not a real solution.

@rbrlortie
Copy link

rbrlortie commented Mar 1, 2019

I trust your judgment, you know the codebase more than I do.

@DanielRuf
Copy link
Contributor

Also see #10949 for information about our event naming scheme.

@DanielRuf
Copy link
Contributor

DanielRuf commented Mar 1, 2019

I will probably prepare a PR based on #8221 (comment)

https://learn.jquery.com/events/event-extensions/

A common convention to avoid collisions for custom events is to embed a colon or dash in the event type name, since no W3C events use those characters

would be a breaking change

@DanielRuf
Copy link
Contributor

I think we should continue the discussion in #11708 to get an opinion from the other maintainers and contributors who also work on projects like FoundationPress - so we can prevent that we break many things and can maybe provide an optin solution for v6.6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants