WordPress.org

Make WordPress Core

Opened 3 years ago

Last modified 11 months ago

#16494 new enhancement

Remove "no conflict" mode from bundled jquery

Reported by: lloydbudd Owned by:
Milestone: Awaiting Review Priority: low
Severity: minor Version: 3.1
Component: External Libraries Keywords: has-patch
Focuses: Cc:

Description (last modified by lloydbudd)

Remove "no conflict" mode from jquery

Over the last six months more and more of the themes I review for large sites have being using Google's CDN version of jquery (deregistering the bundled), not to get the benefit of CDN, but because most jquery libraries are dependent on $ and the developers don't know or want to deal with getting them to work in "no conflict".

I image we originally put in place "no conflict" mode for bundled jquery, as part of the transition from Prototype.

I appreciate it is hard to measure the impact of this type of change, but wanted to trac this all the same.

Additional Info

http://codex.wordpress.org/Function_Reference/wp_enqueue_script#jQuery_noConflict_wrappers

http://docs.jquery.com/Using_jQuery_with_Other_Libraries

Attachments (1)

16494.1.diff (91.8 KB) - added by atimmer 11 months ago.

Download all attachments as: .zip

Change History (41)

comment:1 nacin3 years ago

This has the potential to break a lot. The $ variable is fought over by a few different libraries, Prototype definitely being one.

While I don't know the history, what we need is education for these developers. I don't think dumbing down our API is the best way to do it.

comment:2 follow-up: filosofo3 years ago

I think Lloyd's suggestion is a good one. Working with theme designers, the most common JS error I see is related to their using jQuery plugins that assume $ is the jQuery object even in global scope.

And Prototype's been basically dead for years now.

The problem with educating developers is that typically the assumption is upstream, both in the plugin libraries and in the web tutorials, and the people using them don't have the knowledge necessary to make the changes.

comment:3 lloydbudd3 years ago

  • Description modified (diff)

comment:4 in reply to: ↑ 2 ; follow-up: ericmann3 years ago

Replying to filosofo:

I think Lloyd's suggestion is a good one. Working with theme designers, the most common JS error I see is related to their using jQuery plugins that assume $ is the jQuery object even in global scope.

I disagree entirely. Any jQuery plugin that makes that assumption (that the global $ is an alias for jQuery) isn't a properly written jQuery plugin. Even the most basic tutorials available from the jQuery team start plugins with jQuery.fn.myplugin = function() { ... }.

And Prototype's been basically dead for years now.

Prototype isn't the only library that uses $. So does MooTools. And there are plenty of plug-ins out there that depend on MooTools.

The problem with educating developers is that typically the assumption is upstream, both in the plugin libraries and in the web tutorials, and the people using them don't have the knowledge necessary to make the changes.

The problem with educating developers is that no one bothers to educated developers. We make the assumption that people won't learn and try to protect them from their own incompetence by making systems "idiot proof."

jQuery is a well-known and widely-used library. It's also well-written enough that you can turn off the $ alias with a single line of code when you first start things out. It's easy to remap jQuery to $ as well if you need it, and most of your theme's script will need to fire on the page's ready event anyway ...

jQuery(document).ready(function($){
    ...
});

comment:5 in reply to: ↑ 4 filosofo3 years ago

Replying to ericmann:

Prototype isn't the only library that uses $. So does MooTools. And there are plenty of plug-ins out there that depend on MooTools.

I agree that MooTools presents a problem. What a strange and infelicitous bandwagon for these library authors all to jump on.

The problem with educating developers is that typically the assumption is upstream, both in the plugin libraries and in the web tutorials, and the people using them don't have the knowledge necessary to make the changes.

The problem with educating developers is that no one bothers to educated developers. We make the assumption that people won't learn and try to protect them from their own incompetence by making systems "idiot proof."

The point I was trying to make is that the developers are not the ones using WordPress. It's farther down the food chain: theme designers and others who are not programmers of any sort, but who follow copy-paste tutorials to reproduce slick JS effects in their themes. These folks are not going to be able to correct the plugins of some jQuery plugin author upstream, someone who is not even working with WordPress.

It's easy to remap jQuery to $ as well if you need it, and most of your theme's script will need to fire on the page's ready event anyway ...

Save a few characters and do it this way:

jQuery(function($){


comment:6 scribu3 years ago

Getting some popcorn...

comment:7 GaryJ3 years ago

  • Cc GaryJ added

comment:8 aaroncampbell3 years ago

I'm all for being cautious and thinking this through some, but my gut reaction is that I'd like to see no conflict go away. While ericmann is correct that the "most basic tutorials available from the jQuery team" has it right, there are also thousands of other tutorials spread across the web that people follow, which don't. Then people are frustrated because the code that they are copying/pasting doesn't work on WordPress, but does in other places...which is key because it focuses their frustration on WP not on the tutorial author.

Think about a new developer who's code doesn't work until he stops using WordPress's bundled jQuery and starts using Googles. Not to mention it *is* a way to lower the barrier to entry for WP developers, which we always like to do.

Maybe we need to leave an option (a constant or a filter, nothing in the ui) that would allow a user to force it into noConflict like it is now. This way if they needed MooTools instead of jQuery (and they couldn't do something like noConflict in MooTools...I don't know that library very well), they could simply make the adjustment.

@scribu - Pass the popcorn.

comment:9 follow-up: westi3 years ago

noConflict should stay period.

What I would like to do is move the setting of it out from our bundled jQuery and into another script we enqueue - so that if you want to abuse $ you can easily.

comment:10 hakre3 years ago

Probably $$ helps?

comment:11 in reply to: ↑ 9 lloydbudd3 years ago

Replying to westi:

What I would like to do is move the setting of it out from our bundled jQuery and into another script we enqueue - so that if you want to abuse $ you can easily.

That sounds like a great solution!

comment:12 follow-up: aaroncampbell3 years ago

I'm a +1 on that too. Having the option to choose but the default staying as it is sounds like a good balance.

comment:13 in reply to: ↑ 12 johnbillion3 years ago

Replying to aaroncampbell:

I'm a +1 on that too. Having the option to choose but the default staying as it is sounds like a good balance.

Surely that doesn't solve the problem that you yourself pointed out:

Replying to aaroncampbell:

people are frustrated because the code that they are copying/pasting doesn't work on WordPress, but does in other places

If we introduce an option then the default setting needs to change to get any benefit from it. The kind of people who copy and paste jQuery from tutorials are not the kind of people who are likely to hunt down and implement a filter to disable noConflict mode. They want stuff to "just work". Changing noConflict so it's off by default will fix this and also allow more able developers to enable it via the filter.

comment:14 follow-up: scribu3 years ago

Then we should close this as wontfix.

I don't think we should be encouraging that kind of copy/paste "programming", using low quality snippets anyway.

comment:15 scribu3 years ago

I would also like to challenge the initial assumption: "most jquery libraries are dependent on $"

All jQuery plugins that I've came accross expect a "no conflict" environment.

comment:16 in reply to: ↑ 14 filosofo3 years ago

Replying to scribu:

I don't think we should be encouraging that kind of copy/paste "programming", using low quality snippets anyway.

It's not encouragement; it would just be acknowledging the reality of common practice.

Besides, really bad practice is using more than one multipurpose library on the same site, such as both Prototype and jQuery, or MooTools and jQuery. noConflict encourages that.

All jQuery plugins that I've came accross expect a "no conflict" environment.

That's not quite the whole story. Just as an example I picked the first jQuery plugin ordered by most popular first that I could find with documentation (the second-most).

Example use from its docs:

<script type="text/javascript" charset="utf-8">
  jQuery(document).ready(function(){
    $('#my-link, p span').simpleTooltip({
      title: 'me is a tooltip'
    });
  });
</script>

The plugin itself does not assume $, but the example given does.

comment:17 aaroncampbell3 years ago

Replying to johnbillion:

You're right. That doesn't solve the problem but at least it gets us closer. Combine the filter with a simple plugin that uses it and some articles to let people know the solution is available, and you have something that many people COULD do themselves.

Replying to scribu:

I'm not sure exactly what the official stance is, but I actually think we DO want to be "encouraging that kind of copy/paste programming". We want the barrier to entry to be low, and copying/pasting from tutorials and examples is how a lot of people get started.

As for the $ issue, I think filosofo explained it well. Not all code that uses the $ instead of jQuery is bad code. $ is supposed to be an added convenience, so making use of it (especially for code examples, etc) doesn't really mean anything except that they were trying to make things easy.

comment:18 sivel3 years ago

I agree with westi as well, I think noConflict should stay, but give users the option of removing it via deregistering a jquery-noconflict script.

There are a lot of plugins in the repo that load, mootools, prototype, etc. If we remove noConflict altogether and a user has a plugin that loads jQuery and one that loads prototype, or mootools then we just broke a lot of peoples sites.

As for the recommendation about using $$, that would break mootools if someone had that loaded on their site too, since mootools uses both $ and $$.

Sometimes it is not about making a change to make things not as bothersome (think wp_magic_quotes()), but what keeps us from breaking thousands if not hundreds or thousands of sites that rely on plugins to provide a certain functionality that would completely break if we made backwards incompatible changes.

One other idea, is we try to detect loading of other scripts that use $ and if one is found automatically load jQuery noConflict, but I don't really like this idea.

comment:19 azaozz3 years ago

I agree with @westi as well. As long as we distribute Prototype.js in core (despite that it's not used anywhere) we should make sure it loads properly.

However loading another JS file just to add jQuery.noConflict() is not acceptable IMHO. Perhaps we need to (finally) enhance wp_enqueue_script() and friends to handle passing the script source directly (I know something like this is possible to do with wp_localize_script() but perhaps we need a better/clearer way).

Another possibility would be to leave the .noConflict() as is and add $ = jQuery; in the above mentioned cases.

comment:20 jczorkmid3 years ago

I think removing noConflict at this point would be problematic, but I would like to see it 'unbundled' from jQuery file itself. Early versions of my 'Use Google Libraries' plugin (which changes the registered scripts to same version hosted by the the Google CDN) used to enqueue a an additional file with jQuery.noConfict() in it, but 90% of my support requests were to avoid loading an additional script (but since my goal was to remain 100% compatible with stock WordPress I couldn't just drop it).

In the end I used the `script_loader_src` hook to inject a script tag with

  try{jQuery.noConflict();}catch(e){};

before the next enqueued script is rendered to the page.

I'd love to see a hook similar to what's available via wp_localize_script but which allows a simple way to inject some inline javascript directly after a script loads. Then, if the jQuery.noConflict() was injected this way it would be possible to use an unmodified jquery.js and still have it run in noConflict by default.

comment:21 scribu3 years ago

+1 for jczorkmid's suggestion.

comment:22 jczorkmid3 years ago

  • Cc jpenney@… added

comment:23 jczorkmid3 years ago

I knew the idea of moving the noConflict to a hook seemed familiar. There was an accepted enhancement to add this type of hook that seems to have gotten lost in the shuffle: #14853

comment:24 follow-up: azaozz3 years ago

  • Keywords close added

Don't think we should change this. If we remove noConflict() we are basically banning the use of Prototype.js in WordPress. If it's a hook, plugins can remove it and still ban Prototype, i.e. introduce unsolvable JS libraries conflict.

comment:25 in reply to: ↑ 24 ; follow-up: aaroncampbell3 years ago

Replying to azaozz:

Don't think we should change this. If we remove noConflict() we are basically banning the use of Prototype.js in WordPress. If it's a hook, plugins can remove it and still ban Prototype, i.e. introduce unsolvable JS libraries conflict.

I think you make a good point, my only counter point would be that we have a LOT of hook that a plugin can hook into and break WordPress or other plugins. The fact that someone CAN break things doesn't mean that everyone will (although I'm sure there would be a few that do).

  1. I see the best-case scenario (and least back-compat) as this: We remove no conflict and plugins can add it back if they use prototype , mootools, etc.
  2. The second best option I see is the ability of a plugin to remove it. I think it's less likely that a plugin would employ this as part of making it's own JS work, and more likely that there would be a "jQuery remove no conflict" plugin that people could install to try to fix some jQuery that isn't working. Then again I tend toward optimist more than realist.
  3. Leave it as it is. The biggest advantage is that we don't change anything so back-compat is perfect, and also we protect people from one kind of issue (accidentally excluding other JS libraries). The problem is that we usually try to be flexible and allow a developer to do whatever they want. I understand they still can if they include their own jQuery, unregister ours and register theirs, but that's a lot of duplicated code that could be avoided with a single filter.

Having said all that I'm fine with closing this as wontfix, I just wanted make sure I advocated for my preference.

comment:26 in reply to: ↑ 25 ; follow-up: azaozz3 years ago

Replying to aaroncampbell:

... my only counter point would be that we have a LOT of hook that a plugin can hook into and break WordPress or other plugins.

True but all these hooks have legitimate uses where nothing is broken. If we add noConflict() to a hook the only purpose would seem to be for plugins to remove it.

  1. I see the best-case scenario (and least back-compat) as this: We remove no conflict and plugins can add it back if they use prototype , mootools, etc.
  2. The second best option I see is the ability of a plugin to remove it...

If a plugin removes noConflict() and starts using $ for jQuery (in global scope), it would introduce an unresolvable conflict with Prototype.js, etc. There are several ways to wrap jQuery around the JS if needed:

jQuery(document).ready(function($){
...
});

(function($){
...
})(jQuery);

comment:27 in reply to: ↑ 26 ; follow-up: jczorkmid3 years ago

Replying to azaozz:

True but all these hooks have legitimate uses where nothing is broken. If we add noConflict() to a hook the only purpose would seem to be for plugins to remove it.

The hook proposed in #16494 has other valid uses, but this may not be one of them. That said, moving the noConflict call out of jQuery itself would fix the issues caused by poorly coded plugins/themes that de-register jQuery, then re-registering a version that does not have the noConflict embedded in it. The problem with these issues is that the are seldom with the misbehaving plugin, but cause issues for other plugins.

I do think most people assume that the WordPress distributed jQuery is the stock jQuery, not one that has been modified by WordPress.

comment:28 in reply to: ↑ 27 scribu3 years ago

Replying to jczorkmid:

I do think most people assume that the WordPress distributed jQuery is the stock jQuery, not one that has been modified by WordPress.

Agreed. Regardless of anything else, it's just bad practice to mangle a third-party file, when it's just as easy to add the noConflict call after it.

Also note that because of this, using Google as a CDN for jQuery is problematic.

comment:29 westi3 years ago

I don't see what the problem is with moving the noConflict call into a seperate js file is when we by default will concatenate it in core anyway?

Although due to the fact we only concatenate core js files it will possibly play havoc with an external jQuery and output ordering :)

comment:30 nacin3 years ago

Should be inline data rather than a separate file, for front-end enqueue usage.

comment:31 scribu3 years ago

Wow, I can see problems either way:

  • separate JS file: how do you set the dependencies, so that it's always loaded?
  • inline call: will break when external scripts are concatenated
Last edited 3 years ago by scribu (previous) (diff)

comment:32 azaozz3 years ago

There is another possibility too:

<script type="text/javascript" src="...jquery.js>
    jQuery.noConflict();
</script>

This would run the noConflict bit after jQuery is loaded in (I think) all currently supported browsers.

The problem with adding another file (apart from loading a whole file just for one word) is that we will have to guarantee it is right after jQuery when queued and concatenated.

The problem with using an action to output inline JS is that it won't be captured in the concatenated file at the right place (will be printed later).

comment:33 scribu3 years ago

Yeah, that's exactly what I was trying to say. :)

So, I think we only have two choices:

  1. Leave as is.
  2. Remove noConflict() entirely.

comment:34 scribu3 years ago

Besides, it's really easy to undo the effect of noConflict():

window.$ = jQuery;

If you put that in a separate file, you can enqueue it between jQuery and the script that needs it.

comment:35 rhertzog2 years ago

I would also like to see the jQuery.noConflict() moved outside of jquery.js provided by Wordpress.

Debian already has a package for jQuery and we tend to replace embedded javascript libraries by symlinks to the official Debian package. But when we tried this with WordPress, it broke because we accidentally removed the jQuery.noConflict() call...

So I would like to see this call moved somewhere else, possibly directly added by the script loader as suggested in this Debian bug report: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=591799

Thank you for taking this request into consideration.

comment:36 rhertzog2 years ago

  • Cc hertzog@… added

comment:37 WraithKenny2 years ago

Perhaps a function that'd do:

<script>var $bak = $; $ = jQuery;</script>

before the desired jQuery plugin script, then after it:

<script>$ = $bak;</script>

used as such:

wp_jquery_conflict_resolution( 'dollar-dependant-plugin' );

Alternatively, just register jQuery's source as 'jquery-conflict' and register the jQuery.noConflict(); bit as 'jquery' but that depends on 'jquery-conflict'

wp_register_script( 'jquery-conflict', 'jquery.js', ... );
wp_register_script( 'jquery', 'jquery-noconflict.js', array('jquery-conflict'), ... );

this way both are always loaded when 'jquery' is enqueued and it can be concatenated.

comment:38 WraithKenny2 years ago

could also

$scripts->add_data( 'jquery', 'data', 'jQuery.noConflict();' );

after 'jquery' is added in script-loader.php.

Last edited 2 years ago by WraithKenny (previous) (diff)

comment:39 markoheijnen2 years ago

I wouldn't change it because of the reasons given. It shouldn't be changed because other developers doing it wrong.
In my opinion when you write code it should always be wrapped in a jQuery function. and as param you can pass $

atimmer11 months ago

comment:40 atimmer11 months ago

  • Cc atimmermans@… added
  • Keywords has-patch added; close removed

I have added 16491.1.diff, this moves jQuery.noConflict to an inline script, using the patch I made for ticket #14853 (http://core.trac.wordpress.org/attachment/ticket/14853/14853.3.diff).

If the function wp_add_inline_script will be accepted to core the original use case is possible too, in the following way:

add_action( 'wp_enqueue_scripts', 'disable_no_conflict' )
function disable_no_conflict() {
    wp_add_inline_script( 'jquery-core', '$=jQuery;' );
}
Note: See TracTickets for help on using tickets.