El uso de frameworks Javascript aporta muchas ventajas al desarrollo web, facilidad de uso, estandarización, cross-browsing, … pero estas ventajas nos hacer perder el control y el conocimiento del código, sin contar con la carga de funcionalidades extras que no llegamos a usar.
Por estos motivos, llevo ya tiempo pensando en montar un framework Javascript que cubra mis necesidades, algo ligero y completo que me permita tener el completo control sobre el javascript que desarrollo.
Franki.js
Vamos a ver como montar tu propio framework javascript, montando las funciones básicas para posteriormente ir añadiendo las que necesitemos sin alterar el resto del framework.
Franki.js es un nombre que creo le viene que ni pintado, ya que nos vamos a nutrir de trozos de otros scripts con los que montaremos nuestra criatura. Nos sentiremos con el Dr. Frankenstein creando a su criatura 😀
var franki = {
.. opciones ..
};
Extendiendo
La clave de los frameworks Javascript es sin duda la capacidad de extender elementos y así añadir nuevas funcionalidades a los elementos DOM
con los que generalmente trabajamos. La función que es capaz de extender cualquier elemento es realmente sencilla, pero a la vez realmente poderosa.
extend: function(el, opt){
for (var name in opt) el[name] = opt[name];
return el;
},
// Ejemplo
franki.extend(document.getElementById("logo"), {option: 'nueva propiedad'});
Como podemos ver, esta función recibe 2 parámetros, el elemento que queremos extender y las opciones que queremos añadir a dicho elemento.
Recogiendo elementos
La función que usamos más en nuestros scripts es sin duda document.getElementById();
y evidentemente tenemos que reducir el número de letras que hemos de teclear para obtener un elemento pasándole un ID
.
get: function(id){
return franki.extend(document.getElementById(id), franki);
},
// Ejemplo
franki.get("logo);
Aprovechando que ya disponemos de extend()
devolvemos el elemento extendido con las propiedades que franki.js
va a incorporar.
Esta función no hace nada especial, aunque puede extenderse fácilmente con todas las posibilidades que javascript nos ofrece.
Creando e Insercción de Elementos
La creación de elementos DOM es otra funcionalidad que usamos considerablemente en nuestros scripts, por lo que es realmente necesario enmascarar la función document.createElement();
create: function(type){
return franki.extend(document.createElement(type), franki);
},
// Ejemplo
var newDiv = franki.create("div");
insert: function(what) {
return this.appendChild(what);
},
// Ejemplo
franki.get("logo).insert(newDiv);
Seguimos devolviendo el elemento extendido con las propiedades que poco a poco a ir haciendo crecer.
Atributos y Estilos
Los elementos que hemos extendido, necesitan tener la capacidad de devolver y modificar los atributos y estilos que los componen, para ello crearemos 2 funciones más.
css: function(name, value) {
if (!value) return this.style[name];
this.style[name] = value;
return this;
},
// Ejemplo
var oldBorder = franki.get("logo").css("border");
franki.get("logo").css("border", "1px red solid");
attr: function(name, value) {
if (!value) return this.getAttribute(name);
this.setAttribute(name, value);
return this;
},
// Ejemplo
var oldSrc = franki.get("logo").attr("src");
franki.get("logo").attr("src", "NUEVO_SRC");
Como vemos, disponemos de 2 nuevas propiedades que automáticamente se añadirán a los elementos con los que trabajemos.
Gestión de eventos
Los eventos asociados a los elementos es algo que por comodidad y por compatibilidad con navegadores podemos aprovechar para incluir en nuestro framework.
addEvent: function(type, fn ) {
if ( this.attachEvent ) {
this['e'+type+fn] = fn;
this[type+fn] = function(){this['e'+type+fn]( window.event );}
this.attachEvent( 'on'+type, this[type+fn] );
} else
this.addEventListener( type, fn, false );
return this;
},
// Ejemplo
franki.get("logo").addEvent("click", function(){ alert("Click");});
removeEvent: function(type, fn ) {
if ( this.detachEvent ) {
this.detachEvent( 'on'+type, this[type+fn] );
this[type+fn] = null;
} else
this.removeEventListener( type, fn, false );
return this;
},
// Ejemplo
franki.get("logo").removeEvent("click", function(){ alert("Click");});
Estas funciones, sacadas de este post de John Resig, nos sirven para poder trabajar perfectamente con nuestros elementos, aunque podríamos optar por la última revisión de Incoherence Babble.
Encadenamiento
Algo que siempre me ha gustado de los frameworks javascript actuales es la posibilidad de encadenar funcionalidades en la misma línea. Esto además de ayudarte a reducir el peso de nuestros scripts ayuda (a mi por lo menos) a ver más claro el código que estas escribiendo.
La clave de nuestro framework para poder encadenar funcionalidades es SIEMPRE devolver el elemento con el que estamos trabajando. Si nos fijamos en las funciones anteriores, nos devuelven el elemento que estamos usando. De esta forma podemos conseguir cosas como estas.
franki.get("logo").css("border", "2px red solid").addEvent("mouseover", function(){alert("MouseOver");});
Últimos retoques
Necesitamos un alias para evitar la llamada franki.get()
, que es la base del framework. Por ello podemos optar por el famoso $()
, o cualquier otra combinación que nos guste o nos vaya bien.
window.get = el.get;
get("logo").css("border", "2px red solid");
Aclaraciones
Las funciones son muy básicas y únicamente son para que sirvan de ejemplo de por donde empezar a construir tu propio framework, las posibilidades de extensión son infinitas.
El código
Veamos como quedaría todo juntito 😀
var franki = {
get: function(id){
return franki.extend(document.getElementById(id), franki);
},
extend: function(el, opt){
for (var name in opt) el[name] = opt[name];
return el;
},
create: function(type){
return this.extend(document.createElement(type));
},
insert: function(what) {
return this.appendChild(what);
},
css: function(name, value) {
if (!value) return this.style[name];
this.style[name] = value;
return this;
},
attr: function(name, value) {
if (!value) return this.getAttribute(name);
this.setAttribute(name, value);
return this;
},
addEvent: function(type, fn ) {
if ( this.attachEvent ) {
this['e'+type+fn] = fn;
this[type+fn] = function(){this['e'+type+fn]( window.event );}
this.attachEvent( 'on'+type, this[type+fn] );
} else
this.addEventListener( type, fn, false );
return this;
},
removeEvent: function(type, fn ) {
if ( this.detachEvent ) {
this.detachEvent( 'on'+type, this[type+fn] );
this[type+fn] = null;
} else
this.removeEventListener( type, fn, false );
return this;
}
};
// Alias
window.$ = franki.get;
Promoción
¿Has usado este post para crear tu propio framework? Pues usalo tambien para promocionarlo, usa los comentarios, evitar pegar el código, mejor usar un enlace algún lugar donde ver el código/ejemplos/tutoriales/…
Mejoras
Jose nos comenta el problema que puede provocar que franki
disponga de métodos públicos a los que podrían acceder externamente provocando errores al acceder al elemento this
.
franki.css("border", "1px red solid");
// --> Error
Este ejemplo nos arrojaría un error al detectar que this
(franki
) no dispone de las propiedades própias de un HTMLElement
, por ese momento Jose nos propone un sistema para sacar lo métodos de franki
, haciéndolos privados e innaccesibles desde fuera.
var franki = (function(){
var metodosExtendidos = {
get: function(id){
return franki.extend(document.getElementById(id), franki);
},
extend: function(el, opt){
for (var name in opt) el[name] = opt[name];
return el;
},
create: function(type){
return this.extend(document.createElemen(type));
},
insert: function(what) {
return this.appendChild(what);
},
css: function(name, value) {
if (!value) return this.style[name];
this.style[name] = value;
return this;
},
attr: function(name, value) {
if (!value) return this.getAttribute(name);
this.setAttribute(name, value);
return this;
},
addEvent: function(type, fn ) {
if ( this.attachEvent ) {
this['e'+type+fn] = fn;
var oThis = this;
this[type+fn] = function(){oThis['e'+type+fn]( window.event );}
this.attachEvent( 'on'+type, this[type+fn] );
} else
this.addEventListener( type, fn, false );
return this;
},
removeEvent: function(type, fn ) {
if ( this.detachEvent ) {
this.detachEvent( 'on'+type, this[type+fn] );
this[type+fn] = null;
} else
this.removeEventListener( type, fn, false );
return this;
}
};
return {
extend: function(el, opt){
for (var name in opt) el[name] = opt[name];
return el;
},
get: function(id){
return franki.extend(document.getElementById(id), metodosExtendidos);
}
};
}());
Como podemos ver la variable metodosExtendidos
se convierte en privada y es la usada en la función get()
para extender el elemento que queremos usar.
Gracias Jose por la corrección, no había pensado en esa posibilidad.
55 comentarios, 3 referencias
+
#