Goal
There are two programming languages I like: PHP and Javascript. In this mini series, I will explore Javascript and jQuery ,
and build my own library/framework. I won't re-invent the wheel here.
I'll just make a collection of methods and functionality I need, making
use of some functionality in jQuery.
The goal of the first part of my mission is to create a small,
lightweight, self-contained, easily extensible base for my framework.
Recently I created my own little company, called "The Analog Guy". I'll
just call my library "TAG".
Creating the base
One thing I have learned from using other libraries, is that I don't
like initializing an object in Javascript. I just want to include the
script on my page, and it has to work out of the box. I'll use a self-executing anonymous function
to encapsulate my class, initialize it, and add some functionality. We
start with the blueprint of the self-executing anonymous function:
( function ( $) {
// rest goes here
} ) ( jQuery)
This is an anonymous function that will be executed immediately. As
a parameter to this anonymous function, I give the jQuery object. This
makes sure that inside my anonymous function, $ refers to the jQuery
object. Even if on the rest of the page, another library is used that
defines $. That's called the scope of the object.
So well, next thing I want to do, is reserve my own TAG namespace. So I'll add a little code for that:
( function ( $) {
if ( typeof window.Tag == "undefined" ) {
var Tag = window.Tag = function ( ) {
// rest goes here
}
}
} ) ( jQuery)
If another script uses the "Tag" namespace in the global scope, then
that might indicate that the user has accidentally included the tag.js
file twice on the same page. I've made sure that it the namespace only
gets defined just once.
In my goal, I said I was going to build this library on top of
jQuery. I want to make sure that $ is defined, and that it is jQuery. I
don't want to support other libraries at this moment.
( function ( $) {
if ( typeof window.Tag == "undefined" ) {
var Tag = window.Tag = function ( ) {
// We have a dependency on jQuery:
if ( typeof $ === "undefined" || $ !== window.jQuery ) {
alert ( "Please load jQuery library first" ) ;
}
}
}
} ) ( jQuery)
Now that I know that jQuery is available, the next thing I want to
add, is extensibility. I want to keep the base lightweight: only add
the functionality that is needed. All the other functionality, that is
non-essential, should be added via plugins or extensions. This makes it
possible:
( function ( $) {
if ( typeof window.Tag == "undefined" ) {
var Tag = window.Tag = function ( ) {
// We have a dependency on jQuery:
if ( typeof $ === "undefined" || $ !== window.jQuery ) {
alert ( "Please load jQuery library first" ) ;
}
// Object FN will contain all plugins
this .fn = { } ;
/**
* Basic extend functionality. Each new plugin should extend
* the TAG library, and become part of its namespace
*
* @param namespace String - Namespace of the new plugin
* @param obj Object - The new plugin
*
* @return void
*/
this .extend = function ( namespace , obj) {
if ( typeof this [ namespace ] == "undefined" ) {
if ( typeof this .fn [ namespace ] === "undefined" && typeof this [ namespace ] === "undefined" ) {
// extend each namespace with core functionality
$.extend ( obj, this .fn .extFN ) ;
// load the new plugin in the namespaces:
this .fn [ namespace ] = obj;
this [ namespace ] = this .fn [ namespace ] ;
// initialize the new library if necessary
if ( typeof this [ namespace ] .init === "function" ) {
this [ namespace ] .init ( ) ;
}
} else {
alert ( "The namespace '" + namespace + "' is already taken..." ) ;
}
}
} ;
}
}
} ) ( jQuery)
The extend method has two arguments: the name of the namespace the
plugin wants to use, and then the plugin itself. First there is a check
if that namespace is still available. If it is, the first thing I do,
is add some basic functionality to the plugin, that will make life
easier for the developer of that plugin. Don't worry, we'll see
immediately where I add that core functionality. A next step is to
inject the plugin into the base class. For this, I use the "this.fn"
object, because that makes it easier to detect which plugins are
loaded. As a "bonus", I also inject that same plugin in the base
namespace. That makes it more convenient to use the plugin.
"Tag.fn.myPlugin" can be referred to as "Tag.myPlugin". Lastly, if that
plugin needs some automagical setup, it can define the "init" method,
which will get triggered when loading the plugin.
This provides basic extensibility for my framework. The next method
I need, is a utility method. I cannot place it on a plugin, because it
is something basic: a method that recursively sets a value inside an
object
( function ( $) {
if ( typeof window.Tag == "undefined" ) {
var Tag = window.Tag = function ( ) {
// We have a dependency on jQuery:
if ( typeof $ === "undefined" || $ !== window.jQuery ) {
alert ( "Please load jQuery library first" ) ;
}
// Object FN will contain all plugins
this .fn = { } ;
/**
* Basic extend functionality. Each new plugin should extend
* the TAG library, and become part of its namespace
*
* @param namespace String - Namespace of the new plugin
* @param obj Object - The new plugin
*
* @return void
*/
this .extend = function ( namespace , obj) {
if ( typeof this [ namespace ] == "undefined" ) {
if ( typeof this .fn [ namespace ] === "undefined" && typeof this [ namespace ] === "undefined" ) {
// extend each namespace with core functionality
$.extend ( obj, this .fn .extFN ) ;
// load the new plugin in the namespaces:
this .fn [ namespace ] = obj;
this [ namespace ] = this .fn [ namespace ] ;
// initialize the new library if necessary
if ( typeof this [ namespace ] .init === "function" ) {
this [ namespace ] .init ( ) ;
}
} else {
alert ( "The namespace '" + namespace + "' is already taken..." ) ;
}
}
} ;
/**
* Recursively set values in an object
*
* Method allows to set individual settings, without overwriting
* existing other values in the settings.
*
* @param object Object - The object to modify
* @param data Object - New values for settings
*
* @return Object - Current plugin for chainability
*/
this .setObjectData = function ( object, data) {
var path = ( typeof arguments[ 2 ] !== "undefined" ) ? arguments[ 2 ] : [ ] ;
for ( var k in data) {
path.push ( k) ;
if ( typeof data[ k] === "object" ) {
this .setObjectData ( object, data[ k] , path) ;
} else {
var tmpObject = object;
for ( var i= 0 , l= path.length ; i< l- 1 ; i++ ) {
tmpObject = tmpObject[ path[ i] ] ;
}
tmpObject[ k] = data[ k] ;
}
}
} ;
}
}
} ) ( jQuery)
I know jQuery.extend() exists. I just don't think it can do what I
want: set some properties of an object, without touching other existing
properties. The method "setObjectData" I just created will do just
that. It recursively loops through a set of values, and sets these
values on the given object. This method will be used later, for setting
values in a plugin configuration.
For now, I'm happy with that functionality. I don't have an
immediate use for other functionality. So we can initialize our class
now. Since this takes only 1 line of code, I'll immediately add some
more functionality.
There are certain methods, I want to be available in every plugin,
without the plugin having to know the name of my base class. I think
it's a basic need of the plugins, to be as agnostic as possible about
the rest of the world. The fewer the dependencies, the better. If I
ever decide to rename my company, and I rename my base library. Then
the plugin should still work with little or no effort. These methods
are "setConfig", to set values in a plugin config, and "toString", to
automagically determine the name of the plugin.
( function ( $) {
if ( typeof window.Tag == "undefined" ) {
var Tag = window.Tag = function ( ) {
// We have a dependency on jQuery:
if ( typeof $ === "undefined" || $ !== window.jQuery ) {
alert ( "Please load jQuery library first" ) ;
}
// Object FN will contain all plugins
this .fn = { } ;
/**
* Basic extend functionality. Each new plugin should extend
* the TAG library, and become part of its namespace
*
* @param namespace String - Namespace of the new plugin
* @param obj Object - The new plugin
*
* @return void
*/
this .extend = function ( namespace , obj) {
if ( typeof this [ namespace ] == "undefined" ) {
if ( typeof this .fn [ namespace ] === "undefined" && typeof this [ namespace ] === "undefined" ) {
// extend each namespace with core functionality
$.extend ( obj, this .fn .extFN ) ;
// load the new plugin in the namespaces:
this .fn [ namespace ] = obj;
this [ namespace ] = this .fn [ namespace ] ;
// initialize the new library if necessary
if ( typeof this [ namespace ] .init === "function" ) {
this [ namespace ] .init ( ) ;
}
} else {
alert ( "The namespace '" + namespace + "' is already taken..." ) ;
}
}
} ;
/**
* Recursively set values in an object
*
* Method allows to set individual settings, without overwriting
* existing other values in the settings.
*
* @param object Object - The object to modify
* @param data Object - New values for settings
*
* @return Object - Current plugin for chainability
*/
this .setObjectData = function ( object, data) {
var path = ( typeof arguments[ 2 ] !== "undefined" ) ? arguments[ 2 ] : [ ] ;
for ( var k in data) {
path.push ( k) ;
if ( typeof data[ k] === "object" ) {
this .setObjectData ( object, data[ k] , path) ;
} else {
var tmpObject = object;
for ( var i= 0 , l= path.length ; i< l- 1 ; i++ ) {
tmpObject = tmpObject[ path[ i] ] ;
}
tmpObject[ k] = data[ k] ;
}
}
} ;
} ;
window.Tag = new Tag( ) ;
window.Tag .extFN = window.Tag .fn .extFN = {
/**
* Set the settings of a plugin
*
* @param settings Object - New values for settings
*
* @return Object - Current plugin for chainability
*/
setConfig: function ( settings) {
window.Tag .setObjectData ( this .settings , settings) ;
return this ;
} ,
/**
* Automagically determines correct name of a plugin
*
* @return String
*/
toString: function ( ) {
for ( var k in window.Tag .fn ) {
if ( window.Tag .fn [ k] === this ) {
return 'Tag.' + k;
break ;
}
}
alert ( 'Could not determine the name of the plugin' ) ;
return '' ;
}
} ;
}
} ) ( jQuery) ;
That's it for the moment. I'll certainly need more functionality inside the base class, but I will build it was I need it.
Usage
Well, it's a basic class really, there is no real usage for now. The
strength will come from the plugins. To include the library in your
files, simply include script tags which point to the correct JS file.
Writing your own plugins
Since I'm emphasizing plugins, I'll give you the basic structure of
how a plugin should be built. In future parts, I'll build at least two
plugins that will further extend the functionality: a "core" plugin,
and a "pubsub" plugin. Here's the template:
(function(){
var myPlugin = {
/**
* @var object settings - The configuration of this library
*/
settings:{},
/**
* Will be called as soon as this library is loaded
* into the main Tag library
*
* @return void
*/
init:function(){},
// other methods go below
};
Tag.extend("myPlugin",myPlugin);
})();
That's all there is to it. Just an Object Literal notation
of an object, again inside a self-executing anonymous function. Include
your JS file in your HTML, after you included the Tag library, and your
plugin will be available as "Tag.myPlugin".
Next
In the next part of this mini series, I'll create the "core" plugin.
This plugin will create functionality that is quite basic, but not
essential to the library. Usually the methods will be utility methods
which can be used by other plugins or regular Javascript code.
In the last part of the series, I'll introduce my "pubsub" plugin.
It's a plugin that serves as a dispatcher for events. With it, it is
possible to send out event notifications. Other parts of your code will
be able to (un-)subscribe to events, and will get triggered when the
event is fired.
If you have any remarks, please let me know. I'm open to suggestions and improvements. My code is available at http://www.codaset.com/theanalogguy/the-analog-guy-javascript-library .
Language
More
Chinese
Taiwan
Deutsch
Italian
Janpanese
Korean
Turkish
Indonesian
Ukraine
Polish
Spanish
Slovenian
Recent articles Insights for Advanced Zooming and Panning in JavaScript Charts How to open a car sharing service Vue developer as a vital part of every software team Vue.js developers: hire them, use them and get ahead of the competition 3 Reasons Why Java is so Popular Migrate to Angular: why and how you should do it The Possible Working Methods of Python Ideology JavaScript Research Paper: 6 Writing Tips to Craft a Masterpiece Learning How to Make Use of New Marketing Trends 5 Important Elements of an E-commerce Website
Top view articles Adding JavaScript to WordPress Effectively with JavaScript Localization feature Top 10 Beautiful Christmas Countdown Timers Top 10 Best JavaScript eBooks that Beginners should Learn 65 Free JavaScript Photo Gallery Solutions 16 Free Code Syntax Highlighters by Javascript For Better Programming Best Free Linux Web Programming Editors Top 50 Most Addictive and Popular Facebook mini games More 30 Excellent JavaScript/AJAX based Photo Galleries to Boost your Sites Top 10 Free Web Chat box Plug-ins and Add-ons The Ultimate JavaScript Tutorial in Web Design