Javascript: sobreescribir el comportamiento del botón derecho del ratón en una web.

A veces, por lo que sea, en una aplicación web necesitamos sobreescribir el comportamiento standar del click derecho del ratón, que en lugar de abrirnos el menú contextual abra algo personalizado. Veamos lo básico ¿Cómo hacemos que en lugar del comportamiento por defecto ejecute el código que queramos? Pues recogiendo el evento y escribiendo nuestro propio código:

if (document.addEventListener) {
    document.addEventListener('contextmenu', function(e) {
        alert("NANAY DE ABRIR!!!!!"); //Aquí iría el código que quieres ejecutar en lugar del comportamiento por defecto
        e.preventDefault(); //esta línea evita el comportamiento por defecto
     }, false);
 } else {
    document.attachEvent('oncontextmenu', function() {
        alert("NANAY DE ABRIR"); ///aquí una alternativa
        window.event.returnValue = false;
    });
}

Búsqueda binaria o dicotómica

Durante el corto tiempo que tuve cuenta en CuriousCat (lo borré porque para recibir insultos anónimos ya me llega con Tweeter y los comentarios de este blog) alguien me pidió que escribiera sobre la diferencia entre una búsqueda binaria y una búsqueda dicotómica. Eso se lo contesté rápidamente allí: búsqueda dicotómica y búsqueda binaria son sinónimos, no hay diferencia.

¿En qué consiste la búsqueda binaria?

Cuando hablamos de búsqueda binaria hablamos de un algoritmo, una serie de instrucciones para que un programa informático realice una tarea. Concretamente de un algoritmo de búsqueda, es decir, la serie de instrucciones necesarias para encontrar un valor dentro de una colección de valores. La búsqueda binaria está pensada para buscar un elemento dentro de una colección ordenada. Vamos a explicar cómo funciona con un ejemplo: imagínate que tuvieras una lista con todos los habitantes de Barcelona ordenada por la primera letra de su apellido, tienes que buscar a alguien que se apellida «Martínez» ¿te pondrías a mirar la lista desde el principio hasta llegar a ese nombre? Son millones de comprobaciones. Ese procedimiento sería el de una búsqueda lineal: comprobar todos los valores hasta encontrar el deseado, funciona pero es muy lento, tiene utilidad cuando no queda más remedio (una colección desordenada) pero es demasiado ineficiente para una lista ordenada.

Aplicando una búsqueda binaria a ese ejemplo lo que haríamos sería encontrar primero el valor del medio de la lista, dividir la lista en dos mitades y comprobar si el valor intermedio es el que buscamos, es menor o es mayor. Si se diera la casualidad de que es el que buscamos la búsqueda ya estaría finalizada, si el valor intermedio es menor descartaríamos la mitad inferior de la lista y si es mayor descartaríamos la mitad superior. Ahora que localizamos en qué mitad tiene que estar nuestro valor repetimos el procedimiento sobre esa mitad, creando otras dos mitades y de nuevo repitiendo el procedimiento hasta dar con nuestro valor. En cada iteración descartamos la mitad de los datos que teníamos, lo que reduce el tiempo de búsqueda respecto a la lineal.

¿Se usa este algoritmo?

La mayoría de lenguajes de programación o frameworks ya tienen funciones de búsquea integradas muy bien optimizadas, así que no es habitual que alguien tenga que escribir una función de búsquea binaria, excepto en ejercicios académicos para aprender algoritmia. Yo personalmente he utilizado este método para buscar entre conjuntos de datos manualmente, cuando no disponía de un índice. Aquí abajo os dejo una implementación del algoritmo en Javascript:

function buscaDicotomica(valor, conjunto) {
    //valor es lo que vamos a buscar, cojunto es el array donde lo buscamos
    var ini = 0;    //inicio del array
    var fin = conjunto.length - 1;   //fin del array
    var pos = -1;
    var finaliza = false;
    var media;
 
    while (finaliza === false && ini<= fin) {
        media= Math.floor((ini+ fin)/2);
        if (conjunto[media] == valor) {
            finaliza = true;
            pos  = media;
        } else if (conjunto[media] > valor) {  //si está en la mitad inferior
            fin= media - 1;
        } else {  //si está en la mitad superior
            ini= media+ 1;
        }
    }
    return pos;
}

Javascript: cerrar una ventana automáticamente cuando se cierre el diálogo de impresión.

Veamos el caso práctico que me encontré: una aplicación web que para imprimir un documento abre un popup con la previsualización del mismo, cuando pulsas el botón de imprimir debería abrirse el diálogo de impresión y después de aceptar el popup debería cerrarse… pero resulta que si bien en el Safari y el Internet Explorer lo hace correctamente, en el Chrome o en el Firefox se cierra la ventana antes de que se abra el diálogo de impresión. ¿Cómo hacemos para que se cierre en todos los navegadores automáticamente esa ventana tras confirmar el diálogo de impresión?

Pues como el objeto window en Javascript es capaz de reconocer el evento de que se ha cerrado la ventana de impresión (evento onafterprint), bastará con que programemos que se ejecute window.close() cuando se de dicho evento. El código sería:

window.onafterprint = function () {
    window.close();
}

Sobreescribir la función javascript window.alert() nativa del navegador por una personalizada

Resulta me veía con una cuestión trabajosa: en una aplicación web tenía que hacer una ventana de alerta personalizada que sustituyera al window.alert() nativo del navegador. La parte de diseñar la nueva ventana no era tan laboriosa, la labor pesada y repetitiva venía del tener que localizar todas las ocurrencias de window.alert() diseminadas por el código y cambiarlas por la nueva función. Y ahí pensé «¿y si pudiera cambiar el window.alert() por la nueva función?«. Hay un fichero de javascript que está presente en todas las pantallas, así que solo tendría que introducirlo allí.

¿Y cómo reescribimos la función window.alert()? Pues simplemente así:

window.alert = function(){}

Asignando una nueva función a window.alert() sobreescribe su comportamiento nativo por el indicado. Veámoslo de nuevo con más o menos todo el código que lleva:

//alert personalizado, recibe el mensaje a mostrar y el elemento sobre el que poner el foco si es un mensaje de error
window.alert = function(msg,el,isError){
	var left = parseInt((screen.width - 750) / 2);
	var top = parseInt((screen.height-300)/2);
	var ventana = window.open("AvisoNuevo.asp?e="+isError+"&m="+msg,"modal","width=750,height=300,top="+top+",left="+left+",scrollbars=auto,resizable=no,menubar=no,toolbar=no");	
	if(isError && el!=null && el!="" && el!=false){			
		try {
		  document.getElementById(el).focus();
	    }
	     catch (e) { return; }
	}
}

Electron.js: Error «require is not defined» y conflictos entre require y jQuery.

Aquí sigo trabajando en mi primera aplicación corriendo sobre Electron.js y me están saliendo canas por diversos errores que me aparecen a la hora de integrar. Uno de ellos ha sido recibir constantemente el error «require is not defined«. Una de las causas de este error suele ser que la integración con NodeJS está deshabilitada, por seguridad este parámetro viene deshabilitado por defecto para evitar ataques que puedan, por ejemplo, borrar ficheros de nuestro equipo remotamente si cargamos una página no segura. En caso de que estemos cargando una aplicación local podemos ir al main.js y activarlo desde allí configurando las preferencias de nuestro ventana web:

 webPreferences: {
            contextIsolation: false,
            nodeIntegration: true,
            nodeIntegrationInWorker: true,                                                  
            enableRemoteModule: true                                                                     
 }

Es posible que esta configuración nos genere un nuevo conflicto entre jQuery y NodeJS ya que ambos intentan sobreescribir la función require(). Esto puede solucionarse añadiendo esta línea antes de la carga de jQuery:

<script>
		window.nodeRequire = require;
		delete window.require;
		delete window.exports;
		delete window.module;
</script>

Electron.js: obtener el nombre, la versión o la arquitectura de nuestro sistema operativo

Bueno, pues hace unas semanas que en el trabajo estoy currando con el framework Electron.js, que básicamente nos permite transformar aplicaciones web en aplicaciones nativas de escritorio. En mi caso es para actualizar un software de intranet que dependía de ActiveX y de Internet Explorer en exceso. En todo caso vamos a ver algunas cosillas sobre Electron, una de ellas es ¿cómo puedo obtener los datos sobre mi sistema operativo? Pues Electron incluye un módulo para eso:

Lo primero será importar el módulo «os«, para que la aplicación pueda trabajar con el sistema operativo anfitrión. Para el ejemplo crearemos una variable llamada también os (por recordarlo bien) para almacenar dicho objeto:

var os = require("os");

Ahora veamos ¿cómo haríamos para ver la arquitectura de nuestro sistema operativo? Pues llamando al método arch().

var arquitectura = os.arch();

¿Y para ver el nombre del sistema operativo? Pues tenemos dos opciones: el método type() nos devolverá el tipo genérico (Linux, Darwin o Windows) y el método platform() nos devolverá un resultado más concreto (darwin,freebsd,sunos,win32…). Finalmente el método version() nos devolverá la versión del sistema operativo:

var tipo = os.type();
var plataforma = os.platform();
var version = os.version();

Acceder al último elemento de un array en Javascript

Algunos lenguajes nos permiten acceder al último elemento de un array usando el índice en negativo (es decir, poniendo la posición -1) pero no es el caso de Javascript. En ese caso ¿cómo accedo al último elemento de un array? Bueno, si se trata de un lenguaje donde podamos conocer la longitud del array como es Javascript (y la mayoría de lenguajes modernos realmente) basta con usar dicha longitud como índice. En el caso concreto de Javascript tendríamos que usar la propiedad length del array pero restándole una posición (porque nunca hay que olvidar que los arrays empieza en 0):

var ultimoElemento = myArray[miArray.length - 1];

Memoization: Acelerando funciones recursivas. Ejemplo con Javascript.

En su día hablamos aquí sobre la recursividad y sus «peligros«. Las funciones recursivas son muy costosas a nivel de recursos pues al llamarse a si mismas repiten varias veces el mismo paso ¿hay una solución?

Memoization (memorización) es una técnica consistente en almacenar los resultados de funciones muy costosas para devolverlos directamente en lugar de volver a calcularlos.

Por ejemplo, la siguiente función que ya vimos en un artículo anterior nos permitiría calcular un factorial con javascript:

function factorial(num)
{
    if (num < 0) {
        return 0;
    }
    else if (num == 0) {
        return 1;
    }
    else {
        return (num * factorial(num - 1));
    }
}

¿Podemos optimizar mucho esta función cacheando los datos? Podemos:

function factorial(num) {
    // inicializamos si es necesario
    if (!factorial.cache) {
        factorial.cache = {};
    }
    if (num == 0) {
        return 1;
    } else if (num in factorial.cache) {
    // si ya tenemos en cache el factorial de num, lo devolvemos
        return factorial.cache[num];
    } else {
    factorial.cache[num] = num * factorial(num - 1);
    return factorial.cache[num];
}
 

De esta forma nos ahorramos varias llamadas a la función pues simplemente devolvemos un valor almacenado en memoria. Esta no es una técnica exclusiva de Javascript, podemos utilizarla con otros lenguajes de programación. Javascript sí tiene la particularidad de tratar las funciones como un objeto más, eso nos permite por ejemplo definirle la propiedad cache en el ejemplo de arriba (en otros lenguajes deberíamos pasarle como parámetro a la función el array de valores o declararlo como variable global), esto también nos permite hacer que una función reciba como parámetro una función o devuelva como resultado otra función, lo que se conoce como higher-order function o función de orden superior. Sirviéndonos de esto podríamos declarar una función genérica para nuestro memoization:

const memoization = function(func){
    const cache = {};
    return (...args) => {
        const key = [...args].toString();
        return key in cache ? cache[key] : (cache[key] = func(...args));
    }
}

De esta forma podríamos definir nuestra función factorial() tal que así:

var factorial = memoization(function(num) {		
    return (num <= 1) ? 1 : num * factorial(num-1);
})

Javascript: funciones querySelector() y querySelectorAll()

Las funciones querySelector() y querySelectorAll() existen en Javascript como métodos del objeto document. Ambas nos permiten acceder a elementos del DOM utilizando un selector de CSS.

La función querySelector() nos devolverá el primer elemento que coincida con el selector que le pasemos. Si es un id no habría problema puesto que deberían ser únicos en el documento, pero si se tratase de una clase o un elemento HTML entonces nos devolvería solo la primera ocurrencia:

//Si es un id nos devolverá ese elemento
var i = document.querySelector('#Contenedor');

//pero si es un elemento que existe varias veces
//entonces solo tendremos la primera ocurrencia
var x = document.querySelector('p');

//esto pondría el fondo gris al primer p que haya 
//en nuestro documento
x.style.backgroundColor = "#d9d9d9";

¿Y si queremos todos los elementos?

En ese caso tenemos que usar querySelectorAll(). El funcionamiento es igual que el de querySelector(), pero en este caso nos devolverá un objeto Nodelist que contiene todos los elementos que coincidan con el selector que hemos buscado. En este caso no tendría sentido usarlo con id, ya que debería ser un elemento único.

//Aquí tendríamos una lista estática con todos los elementos p
var x = document.querySelector('p');

¿Y cómo accedemos a los elementos del Nodelist?

Al ser una lista podemos acceder a los elementos contenidos en el Nodelist que nos devuele querySelectorAll() mediante un índice. Podemos conocer el tamaño de la lista accediendo a la propiedad length e iterar todos los elementos dentro de un bucle:

//Aquí tendríamos una lista estática con todos los elementos p
var x = document.querySelector('p');

//ahora los recorremos y, para el ejemplo, cambiaremos el color
//de fondo por un gris
var i;
for (i = 0; i < x.length; i++) {
  x[i].style.backgroundColor = "#d9d9d9";
}

jQuery: Poner el foco en el siguiente elemento usando una clase

Os cuento: un cliente pide que en un formulario que está rellenando, con varios campos de texto muy pequeños para un solo carácter, el foco salte de forma automática al siguiente campo cada vez que cubre uno. Es decir, en cada evento de levantar la tecla salta al siguiente. La cosa parecía simple usando la función next() de jQuery:

$(".cuadrito").keyup(function() {
  $(this).next('.cuadrito').focus();	
});

La función next() de jQuery salta al siguiente elemento «hermano» que aparezca. ¿Qué quiere decir hermano? Pues que tengan el mismo padre, esto es que estén contenidos en el mismo elemento del DOM. Y por eso esta práctica y elegante solución no me valía, porque resulta que los inputs están dentro de una tabla, de forma que al cambiar de celda cambia el «padre» en el DOM y ya no salta el foco. ¿Qué hacer entonces? Lo solucioné con la función index():

$(".cuadrito").keyup(function() {	            
  var ind = $(".cuadrito").index(this);
  $(".cuadrito").eq(ind + 1).focus();
});

¿Qué hicimos aquí? Creo que en el pasado ya vimos que jQuery nos permite tratar a los elementos como si de un array se tratase. Simplemente estoy mirando en la primera fila qué índice tendría el elemento en el que nos encontramos y, en la siguiente, poniendo el foco en el posterior sumando 1 al índice.