Calcular el IMC con Javascript

Esta semana en el trabajo me encontraba con una petición de un cliente. En un formulario donde insertaba peso y talla de una persona quería que hubiera un campo con el índice de masa corporal, que debía rellenarse solo. ¿Esto cómo va? Pues lo arreglamos con una función de javascript:

function calculaIMC(){
  //declaramos las variables
  var peso, altura, imc;
  //recogemos los datos.
  //Suponemos que los campos
  //tienen esos Id.
  //El peso en KG y la altura en cm
  //para operar mejor
  peso=document.getElementById("peso").value;
  altura=document.getElementById("altura").value;
  //comprobamos que los campos
  //no vengan vacíos
  if(peso!="" && altura!=""){
    //aplicamos la fórmula
    altura = parseInt(altura)/100;
    imc=peso/(altura*altura);
    document.getElementById("imc").value=imc.toFixed(2);
  }
}

Recuerda que queremos que los datos se introduzcan como tipos enteros: puedes poner una validación por javascript para que no metan comas, usar un campo numeric de HTML5 (hoy por hoy ya todos los navegadores modernos lo soportan) o hasta tirar de una vieja librería de jQuery (que servía para asegurarnos el funcionamiento en navegadores viejos, aunque hoy por hoy ya no tenga mucho sentido).

Javascript: convertir Array en CSV

Vamos con un tip rápido de javascript ¿cómo convertimos un array en una cadena de valores separados por comas? Es bastante sencillo lograrlo, y así obtener un formato válido para crear un fichero CSV más adelante:

var sabbath = ['ozzy', 'iommi', 'bill', 'geezer'];

var sabbathCsv = sabbath.valueOf();

Si lo que queremos son comas como separador el método valueOf() ya nos hace el trabajo, pero ¿si queremos otro separador? Entonces recurriremos a join().

var icada = ['drogas', 'boni', 'alf', 'fer'];

var icadaCsv = icada.join("#");

En el ejemplo de arriba usará el caracter # como separador en lugar de las comas.

Javascript: convertir una string en un número.

La función parseFloat() de Javascript nos permite recoger un dato de tipo string y convertirlo en un número con decimales, mientras que la función parseInt() nos devuelve un entero. ¿Así de simple? Sí, pero con matices. Vamos a profundizar un poco:

En el caso de parseInt() se trata de una función que recibe dos valores: el primero es obligatorio y es la cadena que queremos convertir a entero, el segundo sería un entero entre 2 y 36 para definir la base en que queremos que esté representado el entero devuelto, y es un parámetro opcional que por defecto tomará el valor 10 (base decimal). En implementaciones en navegadores antiguos (por ejemplo IE8) si el número empieza por 0 lo devolverá en base octal, así que ojo. Con los navegadores modernos ya no pasa, pero es importante recordarlo. Si le pasamos una cadena con espacios los obviará y devolverá el número (si hay varios números devolverá el primero que se encuentre). Si le pasamos una cadena con letras y números dependerá de la posición del número: si este está al inicio entonces devolverá el número hasta que encuentre un caracter extraño. Si empieza por letras devolverá un valor NaN. Ejemplos:

parseInt("10"); //devuelve 10
parseInt("10 años"); //devuelve 10
parseInt("10 54"); //devuelve 10
parseInt("10.50"); //devuelve 10
parseInt("casi 10"); //devuelve NaN
parseInt("10",16); //devuelve 16

Si quieres evitar problemas y que te devuelva un 0 cuando el resultado sea NaN ya te lo contamos en su día por aquí.

En el caso de parseFloat() la función sólo recibe la cadena que debe convertir a número decimal. Al igual que parseInt() si recibe una cadena con espacios los ignorará para devolver el número, y si recibe una cadena con caracteres actuará también igual que esa función: si este está al inicio entonces devolverá el número hasta que encuentre un caracter extraño. Si empieza por letras devolverá un valor NaN. En este caso ojo al formato numérico, sólo acepta el anglosajón. Es decir, el separador decimal tiene que ser un punto, interpretará la una coma como un caracter extraño.

parseFloat("10"); //devuelve 10
parseFloat("10.5 años"); //devuelve 10.5
parseFloat("10 54"); //devuelve 10
parseFloat("10.50"); //devuelve 10.50
parseFloat("casi 10.4"); //devuelve NaN
parseFloat("1.000,33"); //devuelve 1
parseFloat("10,50"); //devuelve 10

Entonces ¿qué hago si tengo una cadena donde los decimales vienen separados por comas y los miles marcados con puntos? Pues usando la función replace(). Vamos con un ejemplo:

var strEx = "1.000,33";
//primer paso: fuera puntos
strEx = strEx.replace(".","");
//cambiamos la coma por un punto
strEx = strEx.replace(",",".");
//listo
var numFinal = parseFloat(strEx);

Podría ser útil tener una función creada ya para realizar esta operación de conversión.

Y hasta aquí con esta entrada de Javascript básico.

Las funciones setInterval() y setTimeout() en javascript.

Feliz 2017 a todos, esta es la primera entrada del año. Una cuestión sencilla de javascript ¿para qué valen las funciones setInterval() y setTimeout() y en qué se diferencian?

Pues ambas funciones tienen el mismo objetivo: ejecutar otra función pasado un intervalo de tiempo. ¿En qué se diferencian? En que setTimeout() se ejecutará solamente una vez, mientras que setInterval() lo hará en repetidas ocasiones.

Por ejemplo, podríamos usar setInterval() para que se mostrara en pantalla cuántos segundos llevamos desde que se cargó la página:

/*definimos una función que cada ponga en un
cuadro de texto un valor numérico, sumando uno cada
vez que la invocamos*/
function showSeconds(){
 var val = document.getElementById("tiempo").value;
 document.getElementById("tiempo").value = parseInt(val)+1;
}
/*usamos setTimeout() para que se
ejecute cada segundo (1000 milisegundos como parámetro)*/
setInterval(showSeconds,1000);

O podríamos usar setTimeout() para dar un aviso al usuario a los diez segundos de entrar

/*función que sólo hace un alert*/
function funcAvisa(){
 window.alert("Pareces interesado en esta sección");
}
/*se la llama a los 10 segundos*/
setTimeout(funcAvisa,10000);

Así funcionan los dos temporizadores de Javascript.

ECMAScript 6: Funciones flecha gruesa

Seguimos la serie de artículos dedicados a ECMAScript 6, en este caso con las funciones arrow, funciones flecha o funciones de flecha gruesa. Esta novedad consiste en una sintaxis abreviada para la expresión de función. Además vinculan el valor this contextualmente.

La cuestión de la sintaxist abreviada es simple si vemos un ejemplo:

var a = [
  "Peras",
  "Manzanas",
  "Naranjas",
  "Melones"
];

//Sintaxis de toda la vida
var a2 = a.map(function(t){ return t.length });

//Sintaxis con flecha
var a2 = a.map( s => s.length );

El tema del this contextual es algo más complejo. Seguramente recuerdes que la variable this daba a veces muchos dolores de cabeza ya que pertenecía sólo a un contexto, es decir, cada función definía su propio valor de this. Vamos con un ejemplo de como sería el código old-school:

function Miembro() {
   // Este this es una instancia del constructor Miembro()
   this.experiencia = 0;
   setInterval(function aprender() {
      // Y este this de debajo sería
      //una instancia de la función aprender(),
      //por tanto no podemos usarlo fuera de
      //la función de callback
      //y nos podría generar errores
      this.experiencia++;
   }, 5000);
} 

Teníais la opción de cachear el this dentro de una variable:

function Miembro() {
   var that = this;
   that.experiencia = 0;
   setInterval(function aprender() {
      // Y este this de debajo sería
      //una instancia de la función aprender(),
      //por tanto no podemos usarlo fuera de
      //la función de callback
      that.experiencia++;
   }, 5000);
} 

Otra opción era usar la función bind() para vincular dicha función a this.

Pero con la función de flecha gruesa la cosa es mucho más simple:

function Miembro(){
  this.experiencia = 0;
  //y aquí va la magiar!!!!
  setInterval(() => {
    this.aexperiencia++; //no es un this nuevo, sino que
                       //hace referencia al this de Miembro()
  }, 5000);
}

ECMAScript 6: valores por defecto en las funciones Javascript

Muchos lenguajes permiten definir un valor por defecto en la llamada a una función, pero hasta ahora Javascript no era uno de ellos. Esto cambia con ECMAScript 6, donde podremos hacerlo al igual que en PHP o Python. Os pongo un ejemplillo simple

function EjemploJS(variable="ejemplo"){...}

Esto implicaría que si no se le pasa la variable variable entonces por defecto se aplicará el valor definido en la declaración de la función. Es bastante cómodo porque nos evita hacer pirulas tipo usar un OR cuando la variable viene vacía, que es lo que haríamos en el pasado:

function EjemploJS(variable){
    variable = variable || "ejemplo"
/*lograría el mismo efecto
pero el código es menos limpio*/
}

Próximamente seguiremos con el tema de ECMAScript 6, al que ya dedicamos antaño otra entrada.

Las palabras reservadas let y const en javascript

No conocía la existencia de let, la descubrí ayer porque había usado una variable llamada let en un artículo de 2011 que hablaba sobre cómo validar un DNI con javascript, y me comentaron «let es una palabra reservada«. Mi cara fue como ¿ein? ¿desde cuando? Así que me puse a buscar… y sí, desde la irrupción de ECMAScript 6 en junio de 2015 tenemos una palabra reservada para definir variables que es let,  otra que es const para las constantes.

Sobre const no hay mucho que decir ya que el nombre es muy descriptivo: nos permite definir una constante. ¿Qué es una constante? Pues piensa en una variable pero que sólo puede recibir un valor en el momento de su declaración y que luego ya no puede ser modificada. Te pongo un ejemplo:

/*Declaramos PI como constante*/
const PI = 3.1415926;

/*Esto daría error*/
PI = 3.1416;
/*Porque no se puede volver a dar*/
/*valor a una constante*/

Vamos ahora con let, que tiene más chicha. Citando textualmente la documentación permite declarar variables limitando su alcance (scope) al bloque, declaración, o expresión donde se está usando. ¿Qué diferencia hay con var? Que var nos permite declarar una variable global o una variable local dentro de una función, pero no nos permite limitar su ámbito a un bloque de código concreto. Veamos un ejemplo:

/*Hagamos el ejemplo al viejo estilo*/
/*usando var*/
function PruebaVar() {
  var v = 45;
  if (true) {
    var v = 64;  // es la misma variable
    console.log(v);  // imprime 64
  }
  console.log(v);  // imprime 64
}
/*vemos como el v de dentro del bloque y el de fuera son
la misma variable. Si modificamos su valor dentro del bloque
fuera también está modificado */

/*vamos con let*/
function PruebaLet() {
  let l = 45;
  if (true) {
    let l = 64;  // diferente ámbito
    console.log(l);  // imprime 64
  }
  console.log(l);  // imprime 45
}
/*Con let en cambio la variable l de fuera del
bloque y la de dentro son tomadas como variables
diferentes, y cada una conserva el valor que
se le dio en su ámbito.*/

El uso de let y const sólo está disponible en bloques envueltos en una etiqueta <script type="application/javascript;version=1.7"> o en una versión superior.

Detectando navegadores de móviles (PHP y Javascript)

Es habitual en los tiempos que correr desarrollar una página web para navegadores de escritorio y otras optimizadas para el uso en navegadores móviles (smartphone, tablet). Una maquetación puede parecer cojonuda para una pantalla grande, de un sobremesa, pero ser incómoda en una pantalla de cuatro pulgadas.

En ese caso tenéis dos posibilidades: detectar el dispositivo móvil en el servidor (PHP) o en el cliente (javascript). El ejemplo para PHP sería el siguiente:

<?php
$mobile_browser = ’0';

//$_SERVER['HTTP_USER_AGENT'] -> el agente de usuario que está accediendo a la página.
//preg_match -> Realizar una comparación de expresión regular
if(preg_match(‘/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone)/i’,strtolower($_SERVER['HTTP_USER_AGENT']))){
	$mobile_browser++;
}

//$_SERVER['HTTP_ACCEPT'] -> Indica los tipos MIME que el cliente puede recibir.
if((strpos(strtolower($_SERVER['HTTP_ACCEPT']),’application/vnd.wap.xhtml+xml’)>0) or
((isset($_SERVER['HTTP_X_WAP_PROFILE']) or isset($_SERVER['HTTP_PROFILE'])))){

	$mobile_browser++;
}

$mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'],0,4));
$mobile_agents = array(
‘w3c ‘,’acs-’,'alav’,'alca’,'amoi’,'audi’,'avan’,'benq’,'bird’,'blac’,
‘blaz’,'brew’,'cell’,'cldc’,'cmd-’,'dang’,'doco’,'eric’,'hipt’,'inno’,
‘ipaq’,'java’,'jigs’,'kddi’,'keji’,'leno’,'lg-c’,'lg-d’,'lg-g’,'lge-’,
‘maui’,'maxo’,'midp’,'mits’,'mmef’,'mobi’,'mot-’,'moto’,'mwbp’,'nec-’,
‘newt’,'noki’,'oper’,'palm’,'pana’,'pant’,'phil’,'play’,'port’,'prox’,
‘qwap’,'sage’,'sams’,'sany’,'sch-’,'sec-’,'send’,'seri’,'sgh-’,'shar’,
‘sie-’,'siem’,'smal’,'smar’,'sony’,'sph-’,'symb’,'t-mo’,'teli’,'tim-’,
‘tosh’,'tsm-’,'upg1',’upsi’,'vk-v’,'voda’,'wap-’,'wapa’,'wapi’,'wapp’,
‘wapr’,'webc’,'winw’,'winw’,'xda’,'xda-’);
//buscar agentes en el array de agentes
if(in_array($mobile_ua,$mobile_agents)){
	$mobile_browser++;
}

//$_SERVER['ALL_HTTP'] -> Todas las cabeceras HTTP
//strpos -> Primera aparición de una cadena dentro de otra

if(strpos(strtolower($_SERVER['ALL_HTTP']),’OperaMini’)>0) {
	$mobile_browser++;
}

if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']),’windows’)>0) {
	$mobile_browser=0;
}

if($mobile_browser>0){
//Aquí redireccionamos hacia la aplicación móvil
}else{
//Aquí nos vamos a la versión para escritorio
}
?>

Se trata de una vieja función que econtré hace tiempo y detecta todo tipo de navegadores. La utilicé en mi proyecto de fin de curso y funcionaba. Utiliza un array con referencias a muchos navegadores de dispositivos móviles. La función de javascript por su parte es más sencillita y detectará el SO:

 var ua = navigator.userAgent.toLowerCase();
 var isAndroid = ua.indexOf("android") > -1; // && ua.indexOf("mobile");
 var isiPhone = ua.indexOf("iphone") > -1;
 var isiPod = ua.indexOf("ipod") > -1;
 var isiPad = ua.indexOf("ipad") > -1;
 if(!isiPad) isiPad = /iPad/i.test(ua) || /iPhone OS 3_1_2/i.test(ua) || /iPhone OS 3_2_2/i.test(ua);
 var isWPx = ua.indexOf("windows phone") > -1;

En este caso detecta si se trata de Android, un dispositivo Apple o WindowsPhone.

En último caso, en el siguiente enlace tenéis ejemplos de código actualizados para varios lenguajes. Os puede servir de mucha ayuda.