What's proper jQuery plugin practice? - javascript

Don't be afraid to use any technical jargon or low-level explanations for things, please. I'm savvy enough with computer architecture and low-level programming languages to comprehend any optimizations or memory management techniques, as well as complex structures (classes, member variables, etc.)
My primary focus of code is web-based applications. I work with PHP a lot and I've been learning CSS quickly. Javascript is currently my bottleneck, however. I know enough Javascript to do just about anything sans frameworks (DOM manipulation, AJAX queries, etc.). I also know that I can make my code run quicker, optimize it for specific cases, and I can shrink the over-all size of my code (no external script to include) by manually coding everything. However for ease of reading by other programmers and for speed of coding I'm trying to learn at least one Javascript framework.
After reading through the documentation on a number of frameworks and looking at some tutorials, I preferred jQuery. It allowed for very powerful iterative code in a single line and it had a very small chance of global variable namespace collision. From what I could tell, the ONLY global variable declared is the $ variable and everything else happens within this namespace, and there were even ways to access the namespace without this variable if you wanted to have two frameworks side-by-side. It also had a very small file to include (24 kilobytes gzipped) which means less server load.
My question is what are good practices in creating a jQuery plugin? If I were to start coding websites in jQuery, how should I go about it for the best interoperability and design? I want to ensure that my code can run along-side any other jQuery without interference, it's possible to build plugins off of my code, and I minimize use of the jQuery namespace so that I don't steal variables that might be used by another script.

Read the jQuery plugin authoring suggestions, also look at the unminified jQuery. Notice the last line: window.jQuery = window.$ = jQuery; So there are two global variables window.jQuery and window.$. To delve into this issue a little deeper, read more about the documentation on using jQuery with other libraries and jQuery.noConflict():
// Trigger no conflict mode.
$.noConflict();
// Code that uses other library's $ can follow here.
For writing plugins, make sure to pay special attention to the section called Maintaining Chainability (since jQuery makes use of chainability so nicely). You have to explicitly return this in your plugins to maintain chainability. Additionally, speaking of clashing with other variables, make sure you stop your plugin from clashing with other code by using a closure:
// Use a closure so you can use the dollar sign in your plugin,
// but you don't clash with other uses of the dollar sign in the script
// around where you define your plugin (other libraries, etc.)
(function( $ ){
// Adding your plugin to jQuery
$.fn.yourPlugin = function() {
// Maintain chainability
return this.each(function() {
// ...
});
};
}( jQuery ));
There's a lot of other great information on that plugin authoring page. The above is just the bare bones. There's info on defaults and options, namespacing, and many other things.
Also, if you're concerned about your variables clashing, you can also make use of closures for you own "regular" code... not just jQuery plugins. To do this, enclose your script in a self invoking anonymous function:
(function() {
var ... // these vars will not clash with
// anything outside this anonymous function
// You can do your jQuery in here if you need to access
// the local vars:
$(function() { ... });
}());
For example:
// global 'clash'
var clash = "test";
(function() {
// local 'clash'
var clash = "something else";
// this 'clash' shadows but doesn't change the global 'clash'
}());
alert(clash);
// The global 'clash' has stayed unaffected by the local `clash`
// Output: test
jsFiddle example

You can add plugin methods without using the extend method:
jQuery.fn.myPlugin = function(opts) { ... }
You can use the extend: if you're just looking to extend the jQuery object with multiple
functions you would do:
jQuery.extend({
funcName: function(){
//function code
},
funcName2: function(){
//function code
}
});
"jQuery.extend adds a set of properties to the jQuery object.
jQuery.fn.extend adds a set of properties to the jQuery.fn object
(which is then accessibly via $().foo)."
jQuery.fn is a shortcut for jQuery.prototype.
The official documentation on plugins: http://docs.jquery.com/Plugins/Authoring
Mike Alsup has this good tutorial/pattern discussion:http://www.learningjquery.com/2007/10/a-plugin-development-pattern
EDIT: One other note, be careful of your names with use of packers/minimizers - test on the one of your choice in this regard.

Related

Adobe AEM form with custom javascript events throwing guideLib, guide.js, guideBridge, or guideRuntime undefined or ... is not a function

This is going to be a problem that very few if any people have, but it was time consuming and difficult to solve and i figured it out, so here is the solution, one other person in the world who has this problem.
Are you getting all kinds of weird "this.getOnOffValueFromModelValue is not a function" kinds of errors from guideRuntime.js, guide.js , or guidelib -- or other adobe frontend libraries that you do not control?
These may not apply to you directly but if they do definitely keep reading:
Are you using webpack and including custom code on the page elsewhere (perhaps via a data-sly-use or something)?
Does your project use lodash? or perhaps another library that uses a "_" global variable?
Or perhaps jQuery? or other frontend library?
Then you may be asking questions like:
Why is my form not working properly?
Why is some stuff on the form just not working?
Some rules are just not working... why not and how do i fix them?
Oh I'll feed you, baby birds.
( can someone with a higher reputation add the tags adobe-form, aem-form to this, please)
TLDR:
If you're using Webpack, add this to your module rules:
module: {
rules: [
{ parser: { amd: false } }
// ... your other rules
]
}
or if you're not using webpack, make sure your own variables are not leaking into the global scope, because adobe's are and yours may overwrite theirs or vice-versa.
The explanation that may be applicable even if you're not using webpack that may get you in the right direction:
Adobe's client libraries (like guide.js and guideRuntime and the like) aren't particularly good at dependency injecting their stuff. Their code leaks global variables a lot. For example (see picture below) they use underscore's _.each a lot. if you're using lodash, the function signatures are different-- underscores' allows you to define a "this" context as the third parameter, whereas lodash's doesn't.
So, in our case, our lodash was overwriting their underscore causing a bunch of weird issues... EVEN THOUGH we were NOT declaring lodash globally anywhere. WEBPACK WAS defining lodash globally
So moral of the story, open up console, check to see if any of your libraries are defined globally, and if they are, figure out how to make them not global until Adobe decides to fix their stuff (don't hold your breath).

Can't get $ to work using RequireJS and MooTools

I'm attempting to build a site using a combination of RequireJS and MooTools. It's my first time using both libraries. There is plenty of documentation for using RequireJS with jQuery but less for using it with MooTools. I've found only this really. But I'm having some trouble and much of it probably is a result of ignorance; still, perhaps you all can help.
At the moment, I'm just trying to test out basic functionality and understand how I would go about setting this up. Here is what I have tried:
In my site footer, I have this script tag:
<script src="assets/js/vendor/require.js" data-main="../app.js"></script>
This loads requirejs with the file app.js. Inside app.js I mainly deal with paths:
requirejs.config({
"baseUrl": "assets/js",
"paths": {
"mootools": "//ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed"
}
});
// Load the main app module to start the app
requirejs(["main"]);
Finally, in main.js, I have (so far) the following:
define(["mootools"], function($) {
var a = $$('.menu'); // .menu is a nav menu in the DOM
console.log(a);
var b = $('.menu');
console.log(b);
});
So here, a works, but b causes an error: undefined is not a function. So there are a couple of questions embedded here. First, can someone tell me what the difference in meaning for $ and $$ with mootools? I gather from this tutorial, that both are used in mootools. Also, why is it that mootools is not mapped to $? As I understand it, with jQuery, this is how you would do this, see here for example.
I'm sure there are some basic confusions here, but please have mercy. I'm a newbie to these tools.
MooTools (as is) is not AMD-compliant. David Walsh is cool but he does not like or use RequireJS. The info in his post is well out of date and not practical any more. In fact, I believe none of the MooTools-core team likes AMD or uses it. Anyway, that's beside the point. jQuery now IS based around AMD so using it is easy. MooTools tried it 2 years ago - https://github.com/arian/mootools-core/tree/1.5amd - and gave up. 1.5 is still not out (hopefully next week, still no AMD).
Anyway
You cannot do this quite in that fashion by expecting the script to magically return $ where a module has not been defined.
There is another issue here which is with the fact that you are loading a remote script and that you leave the protocol to be determined automatically - which are sort of quirky things for RequireJS to handle in their own accord.
Two or three ways to handle it.
you can just define a local module, eg your own mootools.js
define([
'http://ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed.js'
], function(){
return window.$;
});
then use by requiring it:
require(['mootools'], function($){
$(document.body).adopt(new Element('div[html=hi]'));
});
eg. http://jsfiddle.net/dimitar/5zYnW/
however, mootools will export all sorts of globals anyway, so it's not really useful. you are better off using the requirejs shim config.
shim example
require.config({
paths: {
mootools: 'https://ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed'
},
shim: {
mootools: {
exports: '$'
}
}
});
// some code.
require(['mootools'], function(){
document.id('foo').adopt(new Element('div[html=hi]'));
});
eg: http://jsfiddle.net/dimitar/5zYnW/1/
old school
I find that it's easier to load MooTools before RequireJS and assume it's all global in all modules that I write - it makes more sense as there are too many global exports to catch. eg. Class, Element, Request etc etc.
eg. https://github.com/epitome-mvc/Epitome/blob/master/example/js/model-demo-require.js -s from my MooTools MVC framework Epitome.
Here's example module code via a UMD wrap - https://github.com/epitome-mvc/Epitome/blob/master/src/epitome-model.js - the only code that implicitly requires MooTools is the node.js code.

How do I noConflict the requireJS require/define methods?

Looking for requirejs.noConflict(), or some way to remove the global footprint from the library. My use case is that I'm writing a widget to run in a 3rd party page which might already define require and define (with a previous version of requirejs or even another library entirely).
I've tried implementing this myself and it mostly works. For simplicity lets assume I do not restore original values, only remove the footprint. I do something like this in my data-main:
var mycontext = requirejs.config({context : 'asdf', baseUrl : 'http://foo.com' });
mycontext(['require','foo'], function (require, foo) {
var f = require('foo');
});
// namespace cleanup
window.requirejs = undefined;
window.require = undefined;
window.define = undefined;
The problem here is that the f value from require('foo') returns null, but only iff I do my namespace cleanup. This smells like a bug in requirejs, but I'm hoping there's an official no-conflict solution.
Hard to google this one due to all the jQuery related noConflict() questions.
The RequireJS documentation has a section on this titled "HOW CAN I PROVIDE A LIBRARY TO OTHERS THAT DOES NOT DEPEND ON REQUIREJS?"
You can also have a look at the example build file, which shows how wrap can be used.
A good example of how this works is the Flight library.
By default the library uses AMD but there is a standalone version that wraps all the modules in an anonymous function and includes a tiny require/define API at the top (They use their own build tool for this). Source can be found here.
Note that for this to work all the modules will require IDs, but the r.js optimiser will handle that for you.
Using these techniques should allow you to leave any already defined require/define variables alone.

Best practice to combine different Javascript SDKs callbacks

I've came across this situation a few times before and always found a tricky way to make it work but I'd like to know if there's a best practice for that:
Sometimes you have to use several JavaScript SDKs on a page example: Google jsapi and jQuery.
Google calls this function when the SDK is ready to be used:
google.setOnLoadCallback(MyFunction);
jQuery does everything in this callback:
document.ready();
What if I want to manipulate the dom using jQuery AFTER Google's callback.
What's the best way to tell the browser: "Wait for jQuery AND Google to be ready and do the stuff..."
Is it best to have the jQuery callback nested inside Google callback? Or the other way round? I'm a bit confused.
You can do this in different ways, one of which is to use a global variable;
var googleLoaded = false;
google.setOnLoadCallback(function(){
googleLoaded = true;
mainCallback();
});
document.ready(mainCallback);
function mainCallback(){
if(googleLoaded){
...
...
}
}
As a general solution, you can do as given below
var jsLibsToLoad = 5;
jsLib1.onLoad(mainCallback);
jsLib2.onLoad(mainCallback);
jsLib3.onLoad(mainCallback);
jsLib4.onLoad(mainCallback);
jsLib5.onLoad(mainCallback);
function mainCallback(){
if(!(--jsLibsToLoad)){
...
...
}
}
I would recommend you to use some AMD tool like RequireJS. RequireJS is a JavaScript file and module loader, and it is really good with dependancy management. In your particular case (using of some traditional/legacy "browser globals" scripts) you should additionally use Shim Config.
At the start using AMD is a little bit tricky, but it gave a great benefits when you get used to it. It will improve the speed and quality of your code.

How to expose javascript objects for unit testing without polluting the global namespace

I have a javascript autocomplete plugin that uses the following classes (written in coffeescript): Query, Suggestion, SuggestionCollection, and Autocomplete. Each of these classes has an associated spec written in Jasmine.
The plugin is defined within a module, e.g.:
(function(){
// plugin...
}).call(this);
This prevents the classes from polluting the global namespace, but also hides them from any tests (specs with jasmine, or unit-tests with something like q-unit).
What is the best way to expose javascript classes or objects for testing without polluting the global namespace?
I'll answer with the solution I came up with, but I'm hoping that there is something more standard.
Update: My Attempted Solution
Because I'm a newb with < 100 xp, I can't answer my own question for 8 hours. Instead of waiting I'll just add what I did here.
In order to spec these classes, I invented a global object called _test that I exposed all the classes within for testing. For example, in coffeescript:
class Query
// ...
class Suggestion
// ...
// Use the classes
// Expose the classes for testing
window._test = {
Query: Query
Suggestion: Suggestion
}
Inside my specs, then, I can reveal the class I'm testing:
Query = window._test.Query
describe 'Query', ->
// ...
This has the advantage that only the _test object is polluted, and it is unlikely it will collide with another definition of this object. It is still not as clean as I would like it, though. I'm hoping someone will provide a better solution.
I think something like the CommonJS module system (as used by brunch, for example) would work.
You can separate your code into modules, and the parts that need them would import them via require. The only part that gets "polluted" is the module map maintained by the module management code, very similar to your test object.
In Autocomplete.coffee
class exports.Query
// ...
class exports.Suggestion
// ...
and then in Autocomplete.spec.coffee
{Query, Suggestion} = require 'app/models/Autocomplete'
describe 'Query', ->

Categories