jueves, 23 de abril de 2009

Colocar un menú que siga al scroll con Javascript

Siguiendo con la anterior entrada, hay un "plus" de las propiedades ya expuestas, aplicadas al objeto body...

Si bien en todos los navegadores salvo IE es posible colocar un elemento "atado" a la posición superior del navegador, sin importar lo que movamos el scroll con position:fixed este estilo es completamente ignorado en IE, por lo que si queremos una compatibilidad total debemos usar, una vez más, Javascript.

En primer lugar hay que obtener una referencia a body. Para ello a partir de cualquier referencia de elemento obtenida con getElementById realizamos elemento1.offsetParent y esto nos devolverá una referencia al body. A continuación dispondremos de estas propiedades:
  • body.scrollLeft que nos devolverá la posición izquierda del área visible de scroll.
  • body.scrollTop que nos devolverá la posición superior del área visible de scroll.
  • body.scrollWidth que nos devolverá el ancho total de la zona scroll.
  • body.scrollHeight que nos devolverá el alto total de la zona scroll.
  • body.offsetWidth que nos devoverá el ancho total del viewport del body, incluyendo el scroll vertical.
  • body.offsetHeight que nos devolverá el alto total del viewport del body incluyendo el scroll horizontal.
Basta con coger el elemento elemento1 y cargar body.scrollTop en su style.top así:
  • elemento1.style.top=body.scrollTop.toString()+"px";
Ejecutamos periodicamente esa orden y ya tenemos un menú atado a la parte superior del viewport del navegador compatible con cualquier browser.

Feedback de posición y tamaño en Javascript

Uno de los problemas de maquetar con position:absolute es que si tenemos elementos flexibles no será fácil hacer que nuestro diseño se ajuste a ellos y tendremos que optar por la maquetación con float:left, float:right y clear:both.

Una manera alternativa de poder maquetar con position:absolute, siempre que no tengamos que cumplir las normas WAI, es utilizar javascript para recolocar los elementos. Las características que voy a explicar, por otro lado, son perfectamente compatibles con todos los navegadores de PC y gran parte de los navegadores de dispositivos portátiles, siempre que tengamos soporte para Javascript activado.

Una vez hemos colocado un elemento con position:absolute a priori será muy dificil -al menos hasta donde llega lo que he visto por internet- obtener el alto si sólo hemos prefijado el ancho. Es posible, mediante técnicas, colocar un bloque justo a continuación del bloque position:absolute, pero para ello hay que cumplir una serie de requisitos que hacen impracticable la solución en la mayoría de los casos.

Pero gracias a varias propiedades accesibles desde Javascript podremos obtener el alto una vez inyectado el contenido dinámico, sin problemas y -por fin- ajustar el resto de los bloques de la web a estas nuevas dimensiones.

El primer paso es obtener la referencia al bloque que queremos, para ello usaremos:
  • var elemento1 = document.getElementById("id_del_bloque");
A continuación, si el bloque es un div (y en otros casos también) dispondremos de las propiedades offset, incluso para elementos con posicionamiento float, relativo o sin position, concretamente:
  • elemento1.offsetLeft nos devolverá la posición izquierda dentro del canvas de la página en píxeles.
  • elemento1.offsetTop nos devolverá la posición superior.
  • elemento1.offsetWidth nos dará el ancho, y, por fin,
  • elemento1.offsetHeight nos devolverá el alto, una vez tenido en cuenta el contenido.
Con elemento1.offsetHeight+elemento1.offsetTop ya podremos obtener un valor para inyectar, una vez hechos los cálculos oportunos (teniendo en cuenta la posición del padre, si lo hay) en elemento2.style.top, donde elemento2 se obtiene con document.getElementById igual que la referencia a elemento1.

Estas propiedades son compatibles con IE, Firefox, Safari, Chrome, Opera y otros muchos navegadores.

miércoles, 22 de abril de 2009

Programación funcional con ANSI-C

Últimamente parece que se está poniendo de moda la programación funcional, sobre todo desde que por un lado los procesadores multi-threading están a la orden del día y desde que C# lo soporta "oficialmente".

Hace mucho no obstante que es posible usar programación funcional, incluso antes de que ningún lenguaje funcional naciera, C ya soportaba callbacks, con los que se podían llegar a hacer cosas similares a las que hoy en día es el pan de cada día en la programación funcional: las funciones lambda.

Supongo que muchos ya conocereis su existencia y cómo usarlas, pero por si acaso no es así os dejo aquí un código de ejemplo que usé de pruebas en su día:

#include

typedef int (*operation__prototype) (int, int);
typedef void (*callback__prototype) (int);

int add__implementation(int input1, int input2) {
return input1+input2;
}
int mul__implementation(int input1, int input2) {
return input1*input2;
}
void callback__implementation(int result) {
printf("[Operation done! callback called with result: %d]\n", result);
}

int operation(int input1, int input2, operation__prototype what_operation, callback__prototype what_callback) {
int retv;
retv=what_operation(input1, input2);
what_callback(retv);
return retv;
}

int option1(int arg1, int arg2) {
operation__prototype add_proc = add__implementation;
operation__prototype mul_proc = mul__implementation;
callback__prototype callback_proc = callback__implementation;
printf(
"[Hello world, result of operation ADD is %d.]\n\n",
operation(arg1, arg2, add_proc, callback_proc)
);
printf(
"[Hello world, result of operation MUL is %d.]\n\n",
operation(arg1, arg2, mul_proc, callback_proc)
);
}

int option2(int arg1, int arg2) {
printf(
"[Hello world, result of operation ADD is %d.]\n\n",
operation(arg1, arg2, add__implementation, callback__implementation)
);
printf(
"[Hello world, result of operation MUL is %d.]\n\n",
operation(arg1, arg2, mul__implementation, callback__implementation)
);
}

int main(int argc,char** argv) {
int arg1=4;
int arg2=5;
//printf("[$argc = %d]\n",argc);
//if (argc>=1) { printf("[$0 = %s]\n", argv[0]); }
if (argc<3) {
printf("%s: usage %s value1 value2\n", argv[0], argv[0]); return 0;
}
if (argc>=2) {
printf("[value1 = %s]\n", argv[1]); arg1=atoi(argv[1]);
}
if (argc>=3) {
printf("[value2 = %s]\n", argv[2]); arg2=atoi(argv[2]);
}
printf("\n");
return option2(arg1,arg2);
}