Contenido

Selectores CSS y frameworks actuales

14 ene

+ 18

Estoy haciendo, a modo de trabajo de investigación un selector CSS muy básico para entender como funcionan los desarrollados por los principales frameworks. Aún está muy lejos de llamarse selector ya que únicamente permite hacer búsquedas muy ligeras y no he testado mucho los posibles problemas.

var js2k = {
	cache: {},
	cleanCache: function(){
		this.cache = {};
	},
	trim: function(string){
		return string.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
	},
	get: function(selector, context, cache) {
		var cache = cache || false;
		// Navegadore modernos
		if (document.querySelectorAll) return document.querySelectorAll(selector);

		// Navegadores MUY antiguos
		if (!document.getElementsByTagName) { return new Array();}
		if (cache && this.cache[selector]) return this.cache[selector];

		var tokens = selector.split(' ');
		var context = new Array(context || document);
		for (var i = 0; i < tokens.length; i++) {
			var found = new Array, h = 0;
			token = this.trim(tokens[i]);

			// ID
			if (token.indexOf('#') > -1) {
				var el = this.getID(token);
				context = el?new Array(el):new Array;
				continue;
			}    

			// Class
			if (token.indexOf('.') > -1) {
				while(h<context.length)
						found = found.concat(this.getClass(token, context[h++]));
				context = found;
				continue;
			}

			// Tag
			while(h<context.length)
				found = found.concat(this.toArray(this.getTag(token, context[h++])));
			context = found;
		}
		if (cache) this.cache[selector] = context;
		return context;
	},
	getID: function(token){
		var bits = token.split('#');
		return document.getElementById(bits[1]);
	},
	getClass: function(token, context) {
		var context = context || document;
		var bits = token.split('.');
		var tagName = bits[0]?bits[0]:"*",className = bits[1], result = new Array;
		if (context.getElementsByClassName) {
			var found = this.toArray(context.getElementsByClassName(className));
			if (tagName == "*") return found;
			var regExp = new RegExp(tagName, "i");
			for (var x in found)
				if (regExp.test(found[x].tagName)) result.push(found[x]);
		} else {
			var t = context.getElementsByTagName(tagName);
	    	var oRegExp = new RegExp("(^|\\s)" + className.replace(/\-/g, "\\-") + "(\\s|$)");
			for (var y in t)
				if (oRegExp.test(t[y].className)) result.push(t[y]);
		}
		return result;
	},
	getTag: function(token, context){
 	    return (context || document).getElementsByTagName(token);
	},
	toArray: function(els){
			return Array.prototype.slice.call(els);
	}
}

Bueno, hasta aquí todo normal, pero cuando he querido hacer pruebas de velocidad para comparar los tiempos con los selectores más rápidos actuales me he encontrado con una curiosidad.

diff_selectors

Si nos fijamos en la imagen superior y buscamos la línea de resultados para la búsqueda de elementos “div p” vemos que todos los frameworks devuelven 140 elementos excepto js2k (si, soy super original xD) que devuelve 142, es un problema de mi script.

El problema lo he podido reproducir en Firefox 3.05, Opera 9,63 (sobre Leopard)  y en Firefox 3.1 , Safari/WebKit el resultado de todos los frameworks, es 140, incluso js2k.

¿Por que?

Pues despues de rebanarme los sesos media tarde he dado con el problema. Al parecer, las estructuras estas no le gustan a mi script:

<div>
	<div>
		<p>Bla</p>
	</div>
</div>
<div>
	<p>Bla</p>
</div>;

Si lanzo una búsqueda sobre esta estructura en Firefox 3.0.5 o Opera 9.63, obtengo 3 elementos <p /> en lugar de 2. Esto se debe a que guardo los elementos <div />; para buscar en cada uno de ellos los siguientes elementos, en este caso los <p />. El fallo está en que mi script almacena 3 <div /> diferentes cuando deberían ser solo 2.

Pero en Firefox 3.1 y Safari/WebKit obtenemos un resultado correcto, osea 2 elementos <p />

He montado un pequeño script donde podemos probarlo nosotros mismo. Solo debería devolver 3 párrafos. ¿Curioso no?

Comentar

#

Me reservo el derecho de eliminar y/o modificar los comentarios que contengan lenguaje inapropiado, spam u otras conductas no apropiadas en una comunidad civilizada. Si tu comentario no aparece, puede ser que akismet lo haya capturado, cada día lo reviso y lo coloco en su lugar. Siento las molestias.