Conflicting jQuery plugins
Permalink 1 user found helpfulOne solution I thought of was to check if the plugin has been loaded already. But the problem with this approach is that while I know how to check in javascript if a certain jquery plugin has been loaded, I don't know how to use that to conditionally include other javascript files (since you include js files from a <script> tag, not from javascript code itself).
Has anyone run into this problem before or have an idea for a solution?
Thanks.
-Jordan
But actually the technique in that thread wouldn't solve the issue I bring up here -- sharing common js between all code in a package is no problem, but what do you do if there's another block or package that's not under your control (or is in the theme itself)?
And the more I think of it, I don't think it's solvable by the CMS, because you don't have any way of uniquely identifying a jquery plugin (for example) by its filename -- it may be "lightbox.js" or "jquery.lightbox.js" or "jquery.lightbox.1.3.2.min.js", etc. etc.
I think the only way to identify a conflicting jquery plugin is in javascript itself, where you can see if the object exists. But unfortunately I don't know of a way to have javascript code conditionally include other javascript code that's stored in another file.
I was addressing only your question about how to include JS, but I think Travis has offered the best solution. I was unaware of that function as well. Thanks Travis!
> what do you do if there's another block or package that's not under your control (or is in the theme itself)?
That's where a dependency system would come in. In my view, all these client-side shenanigans are just kludgy work-arounds.
> you don't have any way of uniquely identifying a jquery plugin (for example) by its filename...
It wouldn't have to be by file name. I was thinking of a mechanism that was well thought out and tightly integrated into the CMS. Maybe the add-on or theme developer would have an official (and well-documented) way of specifying dependencies for their package. Ideally, these dependencies would be resolved at install time - not on the fly during page generation/rendering.
This issue was discussed recently (can't find the thread), and Andrew indicated a dependency system might come about some day, but I'm not holding my breath.
-Steve
You can still do your conditional test and then if it fails, load the plugin with getScript(). It also has a handy callback so that you can then apply the script to your element once loaded.
Below is in crude programming english, but you should get the idea.
// condition test in document.ready() if (object loaded) myfunction(); else $.getScript('path_to_plugin.js', function () { myfunction() }); // end conditional test // // // apply the plugin inside it's own function to avoid duplication of code. function myfunction () { $('your_element').your_plugin_function(); }
Example:
$html = Loader::helper('html'); $this->controller->addHeaderItem($html->javascript('file_name.js', 'package_handle'));
@ijessup -- the addHeaderItem function only "deduplicates" files loaded from the same exact location -- I'm referring to a situation where two different blocks (or the theme header directly) include the same jquery plugin. As mentioned in an earlier comment, even if addHeaderItem did "deduplicate" based on file name (regardless of which block it resides in), you would still have problems because, for example, a jquery plugin file could be called "lightbox.js" or "jquery.lightbox.js" or "jquery.lightbox.1.3.2.min.js", etc.
How are you checking for the existence of the plugin? Assuming you're using something like...
if (jQuery.fn.lightbox) { // It's loaded. } else { // It's not. }
...then it seems agedman might have a good suggestion. I mean, why not adopt this practice as a way of avoiding duplicates? Of course, it doesn't address the issue of plug-in versions, but it's better than nothing.
Also, you say the code won't execute until after the page has loaded. It's not clear to me why that's necessarily the case. Why not put it in a DOM ready callback?
In my case (in the thread I referenced above), my JS code is not a jQuery plug-in, so I basically look at the document's "head" element to see if the script tag (referencing the script) is already there. If not, I append a script element to the document head. Were it a jQuery plug-in, however, I could simply check to see if the plug-in was loaded.
-Steve
And you can't guarantee that the code will run when you want because the jQuery.getScript function makes an asynchronous call (it's just a wrapper function for an ajax call), so the rest of your javascript is running in the browser while the additional file is being retrieved. At least this is what I gather from reading the comments in the jQuery documentation:
http://api.jquery.com/jQuery.getScript/#comment-45819535...
-Jordan
> regardless of version, the plugin will have the same
> name in javascript-land.
But that IS the problem from my perspective. You might want to ensure you're using the latest version, or you might want functionality that's in a specific version.
As for the asynchronous loading issue, I can see where one block can be checking to see of a plug-in is loaded WHILE another is loading the file. That might result in a duplicate. It seems a solution would simply be to load the script synchronously.
-Steve
This is probably like the standards-compliant HTML issues from that other recent thread -- it would be great in theory but would require massive infrastructure upheaval, and I don't really see anyone else here besides me and you running into or caring about these kinds of problems ;)
> and you running into or caring about these
> kinds of problems
I'm not willing to assume no one else cares or that nobody else has encountered this issue just because there aren't lots of posts about it. (It has actually come up before, but I can't find the thread.) Besides, as C5 grows and garners more developers, these types of scenarios certainly aren't going to become LESS frequent.
:-)
-Steve
Although I also understand that there's no easy fix so I'm not holding my breath either.
So if you have a block, template and theme, package them all together.
A problem you might run into is when using multiple blocks/templates/themes from different people that may take advantage of the same plug-in. That's when TravisN's solution will come in handy. Though this issue would seem very unlikely, or at the very least questionable as to why.
It would be nice if the addHeaderItem checked for duplicate functions. I'm sure not a simple task, but would be nice.
But, again... if it is a commonly used JS, put it in a common area. If it is common only to your mods, put it at the package level, other wise, put it at the root level and mod the other packages.
You're correct, though, that it seems quite unlikely to happen "out in the wild" -- I was just looking for a solution for this 1 client site, where I can code up the theme to conditionally include the plugin IF it's not already loaded by a block that they may or may not have placed on any particular page in the site.
As for having addHeaderItem check for duplicate functions, I don't think this is possible because that function just loads files, and does not look inside the files to see what's there (and even if it could do that, you're talking about parsing javascript files on the server which seems like massive overkill for a problem of this small magnitude).
-Jordan
http://drupal.org/project/jq
Perhaps something similar is needed in C5?
-Steve
**Although I note that this wouldn't solve the different version issue.
This way, the only js files that would be included from within a block that aren't hosted on a CDN would be our own unique js libraries, that really shouldn't be duplicated.
I guess we would still run into some issues if we release multiple blocks using a specific library we have written. Maybe the package controller / block installer could be modified so that we can install js file to the root js folder, outside of the package, so the files are always in the same location... Just thinking outloud here...
Jon
A single directory for js installs is also an interesting idea -- but would mean that installable packages aren't self-contained in their own directory.
Not saying that those are bad ideas -- just hashing though the potential issues.
Thanks for the great comment!
-Jordan
I've been using Google's CDN for a while now, and I think it's great. There are a number of benefits - including faster load times for visitors - especially if the file being referenced is already in their cache (more likely the more developers using the CDN).
That said, I still develop with the local copy of jQuery, as a CDN isn't of much use (obviously) if you're not connected to the Net (or happen to be using a painfully slow connection). Additionally, I had some issues with spurious jQuery dialogs appearing while in edit mode due to a conflict with C5's dialog functionality, so I still have to serve jQuery UI from my hosting account until such a time as that issue is resolved.
A single directory isn't a bad idea, but it seems there should be some means of registering libraries (and versions) used by a package. It's not just the libraries themselves at issue here, though. There are a number of popular plug-ins, and it doesn't make sense to load multiple copies of the same plug-in if more than one add-on uses it - hence the need for a registry of sorts.
-Steve
After using CDN for the jQuery, jQueryUI and jQueryUI themes, another advantage of a central script repository is that all other scripts can then be concatenated and compressed into a single file, reducing the number of separate files that need to be loaded. If this file is consolidated across a whole site, then JavaScript load times come way down after a browser has cached it.
Once established, the repository/concatenate/compress could also be used for css.
Or am I missing something - have I failed to find a plugin that already does all this for me?
That is precisely the type of thing I anticipated happening at some point when I first started using C5 and even mentioned it in an early post. Basically, the C5 mandate that "everything" required by a block be included in that block is not ideal for JS libraries and library plug-ins or extensions. It really only makes sense to include truly custom code that's unique to the block (or package). Ideally, C5 should have some type of dependency mechanism that checks for (and even automatically installs) common libraries and shared JS modules that are needed by a block.
Anyway, to answer your question about dynamically loading JS files, that's precisely what I did here if you recall...
http://www.concrete5.org/community/forums/customizing_c5/where-to-p...
-Steve