Contenido

JavaScript Micro-Templating

17 Jul

+ 2

John Resig, ha desarrollado un pequeño sistema de micro-templating para Javascript. Algo que poco a poco parece que se va integrando más en las páginas debido a la relevancia que está cogiendo este lenguaje.

// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
  var cache = {};

  this.tmpl = function tmpl(str, data){
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.
    var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :

      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +

        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +

        // Convert the template into pure JavaScript
        str
          .replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "',$1,'")
          .split("\t").join("');")
          .split("%>").join("p.push('")
          .split("\r").join("\\'")
      + "');}return p.join('');");

    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
})();

Una vez cargado este código, podremos usarlo de la siguiente manera:

//HTML
/* Sin procesamiento en el template */
<script type="text/html" id="item_tmpl">
  <div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
    <div class="grid_1 alpha right">
      <img class="righted" src="<%=profile_image_url%>"/>
    </div>
    <div class="grid_6 omega contents">
      <p><b><a href="/<%=from_user%>"><%=from_user%></a>:</b> <%=text%></p>
    </div>
  </div>
</script>
/* Con procesamiento en el template */
<script type="text/html" id="user_tmpl">
  <% for ( var i = 0; i < users.length; i++ ) { %>
    <li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
  <% } %>
</script>

// JAVACRIPT 
var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);

var show_user = tmpl("item_tmpl"), html = "";
for ( var i = 0; i < users.length; i++ ) {
  html += show_user( users[i] );
}

Como curiosidad, John comenta que el definir los <script type="text/html" /> provoca que el navegador los omita y por consecuente podamos usarlos sin problemas de javascript que podría detectar carácteres no válidos entre estos tags.

liveUpdate para jQuery

9 Jul

+ 4

Primero fué John Nunemaker que desarrolló un sistema de filtrado de listas similar a QuickSilver usando jQuery y acto seguido John Resig decidió meterle mano y modificar el código del liveSearch dejándolo en tan solo 37 líneas de código (frente a las 58 de J.Nunemaker) algo que John Resig comenta con un toque de prepotencia en sus palabras. [Demo J.Resig][Demo J.Numemaer][Descargar versión  J.Resig][Descargar versión J.Nunemaker]

Firefox3 integra opción nativa para gestionar la subida de ficheros

8 Jul

+ 3

Una de las nuevas funcionalidades de Firefox3 es la extensión de los elementos <input type="file" /> que nos permite utilizarlos previamente antes de subirlos a nuestro servidor mediante Javascript.

firefox3_files

Como podemos ver en Firebug, vemos que los elementos <input /> incorporan una propiedad DOM extra llamada files, que se compone de un array de elementos ordenados que hacen referencia a los ficheros subidos mediante ese elemento.

Esta propiedad, dispone de 3 métodos con las que podremos hacer uso de los elementos que subamos:

  • Atributos : fileSize y fileName
  • Métodos: getAsDataURL, getAsBinary y getAsText

Ejemplo de uso

function writeText() {
    var data = $('text').files.item(0).getAsBinary();
    $('result').update(data);
    $('textsize').update($('text').files.item(0).fileSize);
}
function writeImage() {
  var data = $('image').files.item(0).getAsDataURL();
  $('imageresult').src = 'data:' + data;
  $('imagesize').update($('image').files.item(0).fileSize);
}

Tablas de contenido (TOC) con verdadero estilo

8 Jul

+ 1

Los chicos de SmashingMagazine, siempre nos dan motivos para escribir un post. Esta vez es debido a la recopilación de “TOC” (Table Of Content — Tabla de Contenidos (o Indice)) de libros, una buena recopilación de ideas para nuestras aplicaciones.

Estas tablas, suelen se un listado de opciones que el usuario puede encontrarse en nuestra aplicación. Desde QuirksMode recuerdo que ví un script desarrollado en Javascript, que nos permitía crear un TOC usando los elementos H de dicha página.

function createTOC() {
	var y = document.createElement('div');
	y.id = 'innertoc';
	var a = y.appendChild(document.createElement('span'));
	a.onclick = showhideTOC;
	a.id = 'contentheader';
	a.innerHTML = 'show page contents';
	var z = y.appendChild(document.createElement('div'));
	z.onclick = showhideTOC;
	var toBeTOCced = getElementsByTagNames('h2,h3,h4,h5');
	if (toBeTOCced.length < 2) return false;

	for (var i=0;i<toBeTOCced.length;i++) {
		var tmp = document.createElement('a');
		tmp.innerHTML = toBeTOCced[i].innerHTML;
		tmp.className = 'page';
		z.appendChild(tmp);
		if (toBeTOCced[i].nodeName == 'H4')
			tmp.className += ' indent';
		if (toBeTOCced[i].nodeName == 'H5')
			tmp.className += ' extraindent';
		var headerId = toBeTOCced[i].id || 'link' + i;
		tmp.href = '#' + headerId;
		toBeTOCced[i].id = headerId;
		if (toBeTOCced[i].nodeName == 'H2') {
			tmp.innerHTML = 'Top';
			tmp.href = '#top';
			toBeTOCced[i].id = 'top';
		}
	}
	return y;
}

var TOCstate = 'none';

function showhideTOC() {
	TOCstate = (TOCstate == 'none') ? 'block' : 'none';
	var newText = (TOCstate == 'none') ? 'show page contents' : 'hide page contents';
	document.getElementById('contentheader').innerHTML = newText;
	document.getElementById('innertoc').lastChild.style.display = TOCstate;
}

getElementsByClassName versión 2008

4 Jul

+ 7

Robert Nyman publicó en 2005 un artículo en el que mostraba la “Ultimate getElementsByClassName“, una función desarrollada en Javascript que sin necesidad de usar ningún tipo de framework nos permite obtener todos los elementos de una misma clase pasada por parámetro.

Ultimate getElementsByClassName (2005)

function getElementsByClassName(oElm, strTagName, strClassName){
    var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/\-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
    var oElement;
    for(var i=0; i<arrElements.length; i++){
        oElement = arrElements[i];
        if(oRegExp.test(oElement.className)){
            arrReturnElements.push(oElement);
        }
    }
    return (arrReturnElements)
}

Esta funcionalidad que actualmente está contemplada para el estandar y que será integrada en todos los navegadores futuros, nos ahorrará muchas líneas de código cuando esté implementada como parte del núcleo del navegador.
Robert ha desarrollado de nuevo la función, añadiéndole una serie de mejoras que han hecho crecer considerablemente la función:

Ultimate getElementsByClassName (2008)

/*
	Developed by Robert Nyman, http://www.robertnyman.com
	Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/
var getElementsByClassName = function (className, tag, elm){
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
};

Nuevas funcionalidades

  • Utiliza getElementsByClassName nativo si está disponible
  • Utiliza XPath nativo si está disponible
  • Soporta múltiples class en una misma petición
  • Devuelve un array con el que trabajar.

Modo de uso

La función puede recibir 3 parámetros:

  • class: El class que estamos buscando. Obligatorio.
  • tag: El tag que contiene la class. Opcional.
  • elm: Elemento padre desde el que empezar a buscar.Opcional
getElementsByClassName(class, tag, elm);
// Ejemplos
getElementsByClassName("rojo"); // Todos los elementos con class="rojo"
getElementsByClassName("rojo azul grande"); //Todos los elements con class="rojo", class="azul",...
getElementsByClassName("rojo", "p"); //Todas las P con class="rojo"
getElementsByClassName("rojo", "p", document.getElementById("content")); // Todas las P con class="rojo" en el elemento content e hijos.

Para realizar una búsqueda por más de una class, deberemos separarlas por espacios en el parámetro class.

Dragtable, una tabla sortable y dragable (y algún palabro más)

27 Jun

+ 7

Dragtable es una vuelta de tuerca más a las tablas sortables (osea que se pueden ordenar mediante Javascript), con esta además, podemos mover las columnas ordenando los resultados a voluntad.[Demo]

Validar formularios con jQuery

25 Jun

+ 16

Hace unas semanas tuve que desarrollar una función que me permitiera validar formularios mediante Javascript de una forma fácil y rápida. Tenía que ser con jQuery por que es el framework que estamos usando ahora en el trabajo. Una de las premisas era que debido a la aplicación y a la tecnología, necesitaba que fuera lo más flexible posible, por ese motivo opté por el siguiente código:

El código

//Filtros
var filters = {
    requerido: function(el) {return ($(el).val() != '' && $(el).val() != -1);},
    email: function(el) {return /^[A-Za-z][A-Za-z0-9_]*@[A-Za-z0-9_]+\.[A-Za-z0-9_.]+[A-za-z]$/.test($(el).val());},
    telefono: function(el){return /^[0-9]*$/.test($(el).val());}};
// Extensiones
$.extend({
	stop: function(e){
        if (e.preventDefault) e.preventDefault();
        if (e.stopPropagation) e.stopPropagation();
    }
});
// Código
$(document).ready(function(){
	$("form.validable").bind("submit", function(e){
		if (typeof filters == 'undefined') return;
	    $(this).find("input, textarea, select").each(function(x,el){
	        if ($(el).attr("className") != 'undefined') {
	        $.each(new String($(el).attr("className")).split(" "), function(x, klass){
	            if ($.isFunction(filters[klass]))
	                if (!filters[klass](el))  $(el).addClass("error");
	        });
	        }
	    });
		if ($(this).find(".error").size() > 0) {
			$.stop(e || window.event);
			return false;
		}
	    return true;
	});
});
	

Explicación

Si no fijamos en el código podemos ver que este script, será ejecutado en cuando el contenido de la página esté completamente cargado, esto lo conseguimos gracias a $(document).ready(); que jQuery nos ofrece.

Una vez cargado el contenido, se encargará de recorrar todos los elementos y buscará todos los elementos <form class="validable">, a los que le añadirá un listener que será ejecutado en el momento que el formulario ejecute el método submit().

En ese momento, el script recoge todos los element <input />, <textarea /> y <select /> del formulario y revisa las clases de cada elemento. Dependiendo de la clase y si está está definida en el objeto filters, realizará una comprobación u otra.

El objeto filters, se compone de una serie de métodos que serán lanzados para validar cada uno de los elementos, por ejemplo:

//HTML
<input type="text" name="prueba" value="" class="requerido email noanieto" />

//Javascript (Filtros)
var filters = {
       requerido: function(el) {return ($(el).val() != '' && $(el).val() != -1);},
       email: function(el) {return /^[A-Za-z][A-Za-z0-9_]*@[A-Za-z0-9_]+\.[A-Za-z0-9_.]+[A-za-z]$/.test($(el).val());},
       noanieto: function(el){return !"anieto".indexOf($(el).val());}
}

Como podemos ver el elemento <input />, le estamos indicando que es del tipo requerido, email y noanieto. Validaciones que definimos en el objeto filters. En caso de no existir una de las clases indicadas en el elemento <input /> (o cualquier otro), esta será omitida.

Si el elemento no cumple una de las condiciones, añadiremos una clase más al elemento (class="error") y pararemos la ejecución del submit().

Demo

He montado una pequeña demo para verlo funcionar. La podeis probar aqui.

Aclaraciones

Está claro que se trata de una validación Javascript y que su única función es mejorar la experiencia del usuario evitando que llegue a submitar una página con datos erroneos, para hacer las cosas bien, la página receptora, debería hacer la misma comprobación y devolver a la página anterior en caso de error, así los usuarios que no tengan la capacidad de ejecutar javascript, tendrán un resultado igual a los que sí, aunque tengan que esperar a la carga de la página.

Versión para MooTools

Epplestun, me pasa una adaptación del script para MooTools. Muchas gracias!!!

$(document).addEvent('domready', function() {
       $(document.body).getElements('form.validable').addEvent('submit', function(e) {
               if (typeof filters == 'undefined')
                       return;

          this.getElements("input, textarea, select").each(function(el) {
               if (el.getProperty("class").length) {
                       el.removeClass("error");
                               el.getProperty("class").split(" ").each(function(filtro) {
                                       if (!filters[filtro](el))
                                               el.addClass("error");
                               });
               }
          });

          this.getChildren().each(function(el) {
               if(el.hasClass("error")) {
                               e = new Event(e);
                               e.stop();
                       return false;
               }
          });

          return true;
       });

Galería estilo MS. Surface con javascript y canvas

23 Jun

+ 5

Gracias al uso de canvas y el dinamismo de javascript es posible crear una galería de fotos al puro estilo Microsoft Surface.

canvaswithflickr

En ella podremos, además de mostrar imagenes, modificar el tamaño, moverlas por la pantalla y sorprender al usuario con un sistema diferente de visionado de imagenes. Compatible con la mayoría de navegadores modernos no tenemos excusa para, como mínimo probarlo y saber que lo tenemos ahi para lo que pueda hacer falta.

Joose-js, mejorando la programación orientada a objetos

18 Jun

+ 4

Joose-js, es una librería en javascript, destinada a mejorar la programación orientada a objetos de nuestras aplicaciones. Permite la creación de clases de una forma realmente sencilla y elegante:

//Declaración de la clase
Class("Point", {
    has: {
        x: {
            is:   "rw",
            init: 0
        },
        y: {
            is:   "rw",
            init: 0
        }
    },
    methods: {
        clear: function () {
            this.setX(0);
            this.setY(0);
        }
    }
})

// Creamos el objeto y lo usamos
var point = new Point();
point.setX(10)
point.setY(20);
point.clear();

Como podemos ver la claridad en el código es más que evidente, y es de agrader en proyectos en los que el código tiende a degradarse. Además, nos permite crear Módulos, que son una implementación de Namespaces bastante sencilla y fácil de entender.

Texto vertical con jQuery.flipv()

16 Jun

+ 1

Curiosa utilidad desarrollada en Javascript usando jQuery en la que podremos poner texto en vertical en nuestras aplicaciones. Usando el elemento <canvas /> como base para plugin, flipv() nos facilita el trabajo considerablemente.

Ejemplo de uso

// Automáticamente mediante class
<p class='flipv'>Texto vertical</p>
// Manualmente
<p class="textovertical">Texto vertical</p>
<script type="text/javascript">$("p.textovertical").flipv()</script>

Demo y Descarga

Podemos verlo funcionando en este enlace y descargarlo directamente desde la página del autor.