Difference between creating Jquery plugins with $fn and without it - javascript

I'm looking on some Jquery plugins,and I want to build my own. In all tutorials I see syntax like this:
(function($) {
$.fn.myPlugIN = function() {
// Add plugin code here
};
})(jQuery);
But when I look at the code of few popular plugins like iScroll.js, Carousel,js
I see the dont use the $.fn syntax, they write it inside one function
(function(){
function iScroll (el, options) {
var that = this, i;
//some code
}
})();
What are the difference between those function and the use of them?

That's the difference between a jQuery plugin and a basic javascript plugin (or module).
iScroll doesn't use jQuery, it has no reason to be defined as a jQuery plugin. Like most javascript plugins not based on a specific framework, it's based on the very convenient module pattern which lets you define many private variables not cluttering the global namespace. The snippet you show doesn't contain the part where a unique variable is exported in the global namespace.
See source :
if (typeof exports !== 'undefined') exports.iScroll = iScroll;
else window.iScroll = iScroll;

iScroll (and I'm assuming Carousel as well) are not jQuery plugins, so their code-behind syntax will be a bit different.
A jQuery plugin is an extension function that works off jQuery objects. For example, if I create a foo jQuery plugin, I should be able to use that via something like $('#some-element').foo();.
To be able to work this way, jQuery plugins extend the $ prototype by adding functions to the $.fn variable like so:
$.fn.foo = function () {
// foo!
};
iScroll, on the other hand, is not a jQuery plugin. It doesn't work off a jQuery object, so you don't expect it to work like $('#some-element').iScroll();. You use it by invoking iScroll() itself.
var obj = new iScroll('container');
As you'd expect, the difference in means of use will dictate different ways of declaring the plugin in its script file.

Related

After remapping jQuery to $ in my WordPress theme, I can no longer trigger functions from outside the js file

I'm working on a WordPress theme with a lot of jQuery in it. By default, WordPress doesn't allow you to use the $ shortcut and you have to use the full jQuery instead - e.g. jQuery('.class') rather than $('.class').
This isn't too much of a hassle over a few lines of code, but I've got a lot now, so I remapped jQuery to $ using:
(function($){
...my functions here...
})(window.jQuery);
This works fine for functions triggered from within that file, but if I use any inline triggers in the PHP, they no longer work. For example:
<a onclick="loadFullPost('<?=get_permalink()?>?ajax=true','<?=$post->post_name?>',<?=$post->ID?>)">Read more</a>
worked fine before remapping but doesn't now. I can't bind the event as usual within the js file, because I won't be able to access the PHP and WordPress functions I need - unless I'm missing something. For example, this wouldn't work:
$( "#target" ).click(function() {
loadFullPost('<?=get_permalink()?>?ajax=true','<?=$post->post_name?>',<?=$post->ID?>)
});
Is there any way around this?
The issue is that your functions are no longer globals. This is a Good Thing™. (See below for why.)
Is there any way around this?
By far the best way would be to not hook up events like that. Instead, keep your code and markup separate, and hook up your functions using jQuery's on and similar. See below for more.
But if you feel you have to, you can make your functions globals by assigning them as properties on window:
(function($) {
window.loadFullPost = function() {
// ...
};
)(jQuery);
or
(function($) {
function loadFullPost() {
// ...
}
window.loadFullPost = loadFullPost;
)(jQuery);
So how would you do
<a onclick="loadFullPost('<?=get_permalink()?>?ajax=true','<?=$post->post_name?>',<?=$post->ID?>)">Read more</a>
...without using a global function? Like this:
<a class="load-full-post" data-permalink="<?=get_permalink()?>" data-ajax=true data-post-name="<?=$post->post_name?>" data-post-id="<?=$post->ID?>">Read more</a>
and then one handler for them
$(document).on("click", ".load-full-post", function() {
var link = $(this);
// Use the values from link.attr("data-permalink") and such
// to do the work
});
Or if you wanted to use your existing loadFullPost function:
$(document).on("click", ".load-full-post", function() {
var link = $(this);
return loadFullPost(
link.attr("data-permalink"),
link.attr("data-ajax") === "true",
link.attr("data-post-name"),
+link.attr("data-post-id") // (+ converts string to number)
);
});
I should mention that you'll get people telling you to access those data-* attributes via the data function. You can do that, but unless you're using the various additional features of data, it's unnecessary overhead (creating jQuery's cache for the data, etc.). data is not an accessor function for data-* attributes, it's much more (and less) than that.
You can also pass your information as JSON:
<a class="load-full-post" data-postinfo="<?=htmlspecialchars(json_encode(array("permalink" => get_permalink(), "ajax" => true, "name" => $post->post_name, "id" => $post->ID))0?>">Read more</a>
(or something like that, my PHP-fu is weak)
Then:
$(document).on("click", ".load-full-post", function() {
var postInfo = JSON.parse($(this).attr("data-postinfo"));
return loadFullPost(
postInfo.permalink,
postInfo.ajax,
postInfo.name,
postInfo.id
);
});
Why making your functions non-global is a Good Thing™: The global namespace is very crowded, particularly when you're dealing with multiple scripts and plugins and Wordpress itself. The more globals you create, the greater the odds of conflicting with one from another script. By having your functions nicely contained inside your scoping function, you avoid the possibility of stomping on someone else's function/element/whatever and vice-versa.
You can add function from your enclosure to window like that:
(function($){
function loadFullPost(...) {
...
}
window.loadFullPost = loadFullPost;
}).(window.jQuery);
Then your function will be visible for onlick attribute etc.

Best place to attach javascript behaviour to the DOM

I'm writing a Javascript file that provides the functionality to create certain DOM elements with ease.
Coming from a jQuery background, normally I'd make this file a plugin and set it up with:
(function($) {
$.fn.entryPoint = function(options) {
//do stuff
}
})(jQuery);
I have no desire to use jQuery for this script, however.
What's the best way to attach the behaviour I've just written to the DOM in a fashion that has the same meaning as the jQuery plugin notation presented above?
There is no universal best code for anything. It all depends on what you are trying to do. I might do something like this, which gives a single global variable named entryPoint, enclosed so that nothing else leaks out into the global namespace.
(function(w) {
w.entryPoint = function(options) {
//do stuff
}
})(window);

Javascript modules, passing jQuery when it may not be loaded

I'm learning the module pattern for javascript in order to tidy up my code and reduce the need for a long 'global' javascript file.
As a consequence of this, I have a top level 'namespace' module, and a utility module in the same file. The utility module has some functions that require jquery, and others that do not.
On lightweight pages that use no jQuery, I don't want to load the library (I have a very good reason for not doing so).
The problem arises when jQuery is passed as a parameter to the module as in the following:
MODULE.vars = (function (variables,$) {
variables.cdn = undefined; //global for clientside cdn
variables.version = undefined; //global for clientside cdn version
variables.isTouchScreen = undefined; //global for touchscreen status
//Check if jquery exists, if so patch the jquery dependent functions
if ($) {
$(document).ready(function () {
variables.isTouchScreen = $('html').is('.touch');
});
}
return variables;
}(MODULE.vars || {}, jQuery));
The code stops on pages that I don't load jquery, stating that jQuery is undefined - fair enough. If I change the last line to:
}(MODULE.vars || {}, jQuery || false));
the code still complains that jQuery is undefined. I thought, perhaps erroneously, that if jQuery was undefined, it would be passed as undefined in this instance and instead take up the value false (which logic dictates wouldn't be necessary anyway).
How do I get around this problem when jQuery may or may not be present? I attempted to put the following at the top of the script:
var jQuery = jQuery || false;
thinking that this would then take up the value of jQuery if it was loaded. It works in modern browsers with this, but IE8 complains as it gets set to false even if jQuery is being loaded first on a page.
The scripts are all loaded in the correct order in the html, jQuery first, then my module afterwards.
When checking for the cause, IE8 returns $ as an object and jQuery as false. If i do not set the above line in the script, jQuery returns as the same object as $.
Sadly I have to cater for IE8 as well, so how do I get around this issue of the optional presence of jQuery?
Edit: This is only a snippet of the module and there are other functions that depend on jquery, but simply won't get implemented if jquery is not available
I seem to have found an answer that works after I worked out how to implement elanclrs suggestion - I put the following at the top of my modules:
var jQ = jQ || false;
if (typeof jQuery !== 'undefined') {
jQ = jQuery;
}
Then in my module, I pass jQ instead of jQuery.
The reasoning behind the answer was pointed at in this question: Error when passing undefined variable to function?

jQuery plugin class type name

I have searched but have not been able to find any information. I know this isn't typical of jQuery however I need to appease our structure that we have for PHP and make that into the jQuery plugin per my boss.
Is there any way to extend the $.fn to add another name? For example
$.MyTools.useTool('piece of wood','cut');
or
$('#wood').MyTools.useTool('cut');
I guess MyTools would be the class and useTool would be the function. However I have done this in a plugin. We are wanting to have our plugin called MyTools and whenever you use a function in it you need to call MyTools.
Would it be better to do away with the plugin and just create a class?
Yeah, when I want to make a namespace (sort of) like that I do this for all my plugins:
(function( $ ){
if(!$.fn.MyTools) {
$.fn.MyTools = {};
}
$.fn.MyTools.useTool = function() {
// do stuff
};
})( jQuery );
It appears that is no way to do this, so I had to take a different approach and take the official plugin way method by doing $.GLCFormattingCurrency('remove', data);

Can I void all JavaScript calls to $

I need some scripts inside an existing site's scripts.js.
This site has been online for ages, and I can not touch the scripts file.
I am including it standardly in another page. There are numerous jQuery calls in the scripts file. The place I include it does not have jQuery.
I want to void all $() type things. I tried this...
$ = function() { };
before I included scripts.js and it didn't seem to work. I am still getting errors like
$(document) is undefined
Is there a way to void all these jQuery calls?
Thanks
Even if you do get that working, you'll still have problems because the code was written with the assumption that jQuery was present. Yes, you can avoid $ is null or not defined errors on lines like this:
$('div.foo');
But there's no point in just writing that line: there will always be actions on the returned object:
$('div.foo').html('blah');
After the NOP jQuery function, you'll get a "html" is not a function error, and so on. The only way you could do it would be to fill out a skeleton of every possible jQuery method, making sure each one returns itself when appropriate.
...or just rewrite it properly...
try
window.$ = function(selector, context) {alert('eating the calls to $');}
in your file that you're including before the scripts.js file. This is how it's defined in jquery so should take care of the selector syntax.
You may need to define other overrides to cater for the $.method() type calls tho
Well, it's no surprise that $(document) is undefined, since you're not returning a value from your placeholder function. Thus, things like $(document).ready(function(){}); will naturally be errors.
Basically, if I understand right, you need $ to be a function that does nothing and returns another object where calling any member function does nothing. Further, calling member functions of $ itself (e.g. $.ajax()) should have the same behavior.
You can do this with __noSuchMethod__, which is unfortunately non-standard:
window.$ = function()
{
var doNothingObj = new (function()
{
this.__noSuchMethod__ = function()
{
return doNothingObj;
}
})();
return doNothingObj;
};
window.$.__noSuchMethod__ = window.$;
This will allow arbitrary chains:
$(document).ready(function(){});
$("#foo").animate().animate();
$.ajax({ url: "file.html"});
etc.
Of course, a much saner solution is to refactor the code that uses jQuery.

Categories