Conversor de unidades con JavaScript

Hace mucho tiempo en mis inicios en la programación hice un conversor de unidades usando JavaScript, HTML, CSS y los frameworks Bootstrap y jQuery. Era para mi clase de física. Hoy vengo a presentarlo y compartirlo por si a alguien más le sirve.

Conversor de unidades web con JavaScript
Conversor de unidades web con JavaScript

Es un simple convertidor de unidades web que transforma de una unidad a otra. Soporta:

  1. Longitud
  2. Masa
  3. Tiempo
  4. Energía
  5. Frecuencia
  6. Presión
  7. Tamaño de datos
  8. Temperatura
  9. Velocidad
  10. Volumen
  11. Área

A través de este post te mostraré cómo funciona, cómo descargarlo y cómo está hecho.

Unidades soportadas

Soporta todos los grupos de unidades de medidas listadas anteriormente. Por ejemplo en longitud tiene metros, kilómetros, etcétera. En masa tiene gramo, kilogramo, y así para cada categoría.

He escrito todas las unidades y sus equivalencias en un diccionario. Es un archivo grande pues tiene todas las equivalencias como si fuera la base de datos. Se ve así (aunque no es el completo):

/**
 * Created by Luis Cabrera Benito on 27/01/2016.
 */

var unidades = {
    'Longitud': {
        'grupo': 'Longitud',
        'unidades': [
            'kilometro',
            'metro',
            'centimetro',
            'milimetro',
            'micrometro',
            'nanometro',
            'milla',
            'yarda',
            'pie',
            'pulgada'
        ],
        'equivalencias': {
            'kilometro': {
                'kilometro': 1,
                'metro': 1000,
                'centimetro': 100000,
                'milimetro': 1e+6,
                'micrometro': 1e+9,
                'nanometro': 1e+12,
                'milla': 0.621371,
                'yarda': 1093.61,
                'pie': 3280.84,
                'pulgada': 39370.1
            },
            
    }
};

Ahora ese archivo es leído desde el verdadero conversor cuya plantilla HTML es la siguiente en donde cargamos los scripts incluyendo a jQuery.

<!doctype html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="./css/bootstrap.css">
    <link rel="stylesheet" href="./css/fa.css">
    <link rel="stylesheet" href="./css/styles.css">
    <title>Conversor de unidades - Dennis</title>
</head>
<body>
<div class="container-fluid">
    <div class="row "><br>
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
            <div class="form-group">
                <label for="principal">¿Qué quieres convertir?
                    <button class="btn btn-info" id="mostrarEjemplo">Ver un ejemplo</button>
                </label>
                <input type="text" class="form-control" id="principal" placeholder="256 bits a megabytes"
                       autocomplete="off">
            </div>
            <br>
            <div class="alert" id="resultado">
            </div>
            <br>

            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 ocultable" hidden="hidden">
                <div class="row ">
                    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
                        <div class="form-group">
                            <label for="grupoUnidades">Unidad de medida</label>
                            <select name="grupoUnidades" id="grupoUnidades" class="form-control"></select>
                        </div>
                    </div>
                    <br><br>
                    <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">
                        <label for="primeraUnidad">De</label>
                        <input id="inputPrimeraUnidad" type="text" class="form-control">
                        <select name="primeraUnidad" id="primeraUnidad" class="form-control select"></select>
                    </div>
                    <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">
                        <label for="segundaUnidad">A</label>
                        <input id="inputSegundaUnidad" type="text" class="form-control">
                        <select name="segundaUnidad" id="segundaUnidad" class="form-control"></select>
                    </div>
                </div>
            </div>
            <br><br><br>
            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
                <button id="btn-about" class="btn btn-primary form-control"><i
                        class="material-icons fa fa-info-circle"></i> Acerca de
                </button>
            </div>

        </div>
        <div id="acercaDe" class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal">&times;</button>
                        <h4 class="text-center">¡Hola!, me llamo Dennis y puedo convertir unidades.<br>
                            <strong>Nota:</strong>
                            Por el momento no entiendo las abreviaciones como "km" o "m",
                            pero mejoraré día a día. <i class="fa fa-smile-o"></i></h4>
                    </div>
                    <div class="modal-body">
                        <strong>Unidades que puedo convertir hasta el momento:</strong>
                        <ul class="list-group">
                            <li class="list-group-item list-group-item-success">Longitud</li>
                            <li class="list-group-item list-group-item-success">Masa</li>
                            <li class="list-group-item list-group-item-success">Tiempo - Gracias a <strong><a
                                    href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
                                escribir las equivalencias
                            </li>
                            <li class="list-group-item list-group-item-success">Energía - Gracias a <strong><a
                                    href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
                                escribir las equivalencias
                            </li>
                            <li class="list-group-item list-group-item-success">Frecuencia - Gracias a <strong><a
                                    href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
                                escribir las equivalencias
                            </li>
                            <li class="list-group-item list-group-item-success">Presión - Gracias a <strong><a
                                    href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
                                escribir las equivalencias
                            </li>
                            <li class="list-group-item list-group-item-success">Tamaño de datos - Gracias a <strong><a
                                    href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
                                escribir las equivalencias
                            </li>

                            <li class="list-group-item list-group-item-success">Temperatura</li>
                            <li class="list-group-item list-group-item-success">Velocidad</li>
                            <li class="list-group-item list-group-item-success">Volumen</li>
                            <li class="list-group-item list-group-item-success">Área - Gracias a <strong><a
                                    href="https://twitter.com/MariJose961128">Marijo Sacramento</a></strong> por
                                escribir las equivalencias
                            </li>
                        </ul>
                        <br>
                        <p>Más unidades vienen en camino...</p>
                        <p>Creado y mantenido por <a href="https://www.facebook.com/cabrerabenito">Luis Cabrera
                            Benito</a></p>
                        <p>Programado en Javascript utilizando jQuery. Los estilos y algunas funcionalidades son gracias
                            a Bootstrap.</p>
                        <p>Código fuente disponible en GitHub. <a
                                href="https://github.com/parzibyte/dennis-conversor">Ver código</a></p>
                        <hr>
                        <p>Bautizado como Dennis en honor a <strong><a
                                href="https://es.wikipedia.org/wiki/Dennis_Ritchie">Dennis Ritchie</a></strong></p>
                        <blockquote>
                            <p>Vacía tu memoria con un <i>free()</i>, como un puntero.
                                <br>
                                Si casteas un puntero a un <i>entero</i>, se convierte en un <i>entero</i>,
                                <br>
                                si casteas un puntero a un <i>struct</i>, se convierte en un <i>struct</i>.
                                <br>
                                El puntero puede estallar,
                                <br>
                                el puntero se puede sobrecargar,
                                <br>
                                ¡Sé un puntero, amigo mío!
                            </p>
                            <footer>Dennis Ritchie</footer>
                        </blockquote>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
                    </div>
                </div>

            </div>
        </div>
        <div id="manual" class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal">&times;</button>
                        <h4 class="text-center">Cómo hablarle a Dennis</h4>
                    </div>
                    <div class="modal-body text-center">
                        <p>Dennis aceptará que le hables como a una persona con frases como:</p><br>
                        <ul class="list-group">
                            <li class="list-group-item list-group-item-success">1 milla a metros</li>
                            <li class="list-group-item list-group-item-success">15.05 libra por pulgada cuadrada en
                                atmosfera
                            </li>
                            <li class="list-group-item list-group-item-success">28 año natural a siglo</li>
                            <li class="list-group-item list-group-item-success">11 kilohertz a hercios</li>
                            <li class="list-group-item list-group-item-success">96 bits en kilobits</li>
                        </ul>
                        <br>
                        <p><strong>Nota: </strong>Dennis acepta palabras en singular, y en plural (por ejemplo,
                            "metros") pero solamente si
                            la unidad no tiene espacios. Ya que por ejemplo "grados celsius" no será detectado
                            correctamente. En ese caso
                            es mejor escribir "grado celsius".</p>
                        <br>
                        <p>Conforme vayas escribiendo, Dennis te avisará si es que tienes un error</p>
                        <img src="./img/1.png" alt="Imagen 1" class="img img-responsive img-thumbnail">
                        <br>
                        <p>Si Dennis no reconoce una unidad (a veces es porque no reconoce los plurales, por ejemplo
                            "años naturales") te lo dirá.</p>

                        <img src="./img/2.png" alt="Imagen 2" class="img img-responsive img-thumbnail"><br>
                        <br>
                        <p>Una vez que Dennis haya reconocido lo que le dijiste, lo convertirá y te mostrará unos
                            formularios</p>

                        <img src="./img/3.png" alt="Imagen 3" class="img img-responsive img-thumbnail"><br>
                        <p>Dichos formularios son para que interactúes mejor con Dennis, y no tengas que estar
                            escribiendo.</p>
                        <p>Puedes mover y cambiar valores a tu gusto.</p>

                        <img src="./img/4.png" alt="Imagen 4" class="img img-responsive img-thumbnail"><br>
                        <img src="./img/5.png" alt="Imagen 5" class="img img-responsive img-thumbnail"><br>
                        <img src="./img/6.png" alt="Imagen 6" class="img img-responsive img-thumbnail"><br>

                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-success" data-dismiss="modal">Entendido</button>
                    </div>
                </div>

            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript" src="./js/bootstrap.js"></script>
<script type="text/javascript" src="./js/unidades.js"></script>
<script type="text/javascript" src="./js/conversor.js"></script>
</body>
</html>

Escuchamos ya sea que cambie el input o unos de los seleccionadores y se hace la conversión, quedando el código así:

/**
 * Created by Luis Cabrera Benito on 25/01/2016.
 */
$(document).ready(function () {
    principal();
    $selectPrimerGrupo.val('metro');
});
var $inputPrincipal = $('input#principal'),
    $resultado = $('div#resultado'),
    $selectGrupoUnidades = $('select#grupoUnidades'),
    $selectPrimerGrupo = $('select#primeraUnidad'),
    $selectSegundoGrupo = $('select#segundaUnidad'),
    $inputPrimerGrupo = $('input#inputPrimeraUnidad'),
    $inputSegundoGrupo = $('input#inputSegundaUnidad'),
    $btnEjemplo = $('button#mostrarEjemplo'),
    $ventanaAcercaDe = $('div#acercaDe'),
    $btnAcercaDe = $('button#btn-about'),
    contador = 0,
    segundosCambiaPlaceholder = 1,
    limiteLongitudNumeros = 16,
    palabrasPermitidas = ["en", "a"],
    sugerencias = [
        "20 hercios a kilohertz",
        "1 milimetro en pies",
        "14.6 miligramos en onzas",
        "256 bits a megabytes",
        "15 mes a horas",
        "256 bits a megabytes"
    ];
function aLetraCapital(palabra) {
    return palabra[0].toUpperCase() + palabra.slice(1, palabra.length);
}

function cambiaPlaceholder() {
    if (contador < sugerencias.length) {
        $inputPrincipal.attr('placeholder', sugerencias[contador]);
        contador++;
    } else {
        contador = 0;
    }
}

function compruebaValores(oracion) {
    if (oracion) {
        var oracionCortada = cortaOracion(oracion);
        if (isNaN(oracionCortada)) {
            var numero = oracionCortada.numero,
                unidad1 = oracionCortada.u1.toLowerCase(),
                unidad2 = oracionCortada.u2.toLowerCase();
            if (!isNaN(numero)) {
                if (numero.length <= limiteLongitudNumeros - 1) {
                    if (dameGrupo(unidad1) === false) {
                        unidad1 = unidad1.slice(0, -1);
                    }
                    if (dameGrupo(unidad2) === false) {
                        unidad2 = unidad2.slice(0, -1);
                    }
                    var grupoUnidad1 = dameGrupo(unidad1),
                        grupoUnidad2 = dameGrupo(unidad2);
                    if (grupoUnidad1 !== false) {
                        if (grupoUnidad2 !== false) {
                            if (grupoUnidad1 === grupoUnidad2) {
                                convierte(grupoUnidad1, grupoUnidad2, numero, unidad1, unidad2, false);
                            } else {
                                escribeError('Los grupos de unidades no son compatibles: <strong>' + unidad1 + ' y ' + unidad2 + '</strong>');
                            }
                        } else {
                            escribeError('La segunda unidad no existe: <strong>' + unidad2 + '</strong>');
                        }
                    } else {
                        escribeError('La primera unidad no existe: <strong>' + unidad1 + '</strong>');
                    }
                } else {
                    escribeError('Lo siento, solamente se permiten números de <strong>16 cifras</strong> sin incluir el punto decimal');
                }
            } else {
                escribeError('La primera parte de la oración no es un número: <strong>' + numero + '</strong>');
            }

        } else {
            switch (oracionCortada) {
                case 0:
                    escribeError('No entiendo lo que quieres. Recuerda que debes unir con "en" o "a", por ejemplo "1 milla <strong>a</strong> metros"');
                    break;
                case 1:
                    escribeError('No entiendo lo que quieres. Recuerda que no debes <strong>incluir espacios</strong> al inicio');
                    break;
            }
        }
    } else {
        $('.ocultable').hide();
    }
}

function convierte(grupo, grupo2, numero, u1, u2, esDelSegundoInput) {
    var resultado = dameEquivalencia(grupo, u1, u2) * numero;
    if (grupo === "Temperatura") {
        resultado = grados(grupo, u1, u2, numero);
    }
    llenaPrimerGrupo($selectPrimerGrupo, grupo);
    llenaSegundoGrupo($selectSegundoGrupo, grupo2);
    $selectGrupoUnidades.val(grupo);
    if (esDelSegundoInput) {

        $inputPrimerGrupo.val(resultado);
        $inputSegundoGrupo.val(numero);
        $selectPrimerGrupo.val(u2);
        $selectSegundoGrupo.val(u1);
    } else {

        $inputPrimerGrupo.val(numero);
        $inputSegundoGrupo.val(resultado);
        $selectPrimerGrupo.val(u1);
        $selectSegundoGrupo.val(u2);
    }
    escribeResultado(numero, u1, u2, resultado);
}

function cortaOracion(o) {
    var posicionPrimerNumero = o.indexOf(" ");
    if (posicionPrimerNumero > 0) {
        var primerNumero = o.slice(0, posicionPrimerNumero);
        var posicionNexo;
        var nexo;
        var x;
        for (x in palabrasPermitidas) {
            posicionNexo = o.indexOf(" " + palabrasPermitidas[x] + " ", posicionPrimerNumero + 1);
            if (posicionNexo !== -1) {
                break;
            }
        }
        if (posicionNexo !== -1) {
            nexo = palabrasPermitidas[x];
            var unidad1 = o.slice(posicionPrimerNumero + 1, posicionNexo);
            var unidad2 = o.slice(posicionNexo + 2 + nexo.length, o.length);
            return {
                'numero': primerNumero,
                'u1': unidad1,
                'u2': unidad2
            };
        } else {
            return 0;
        }
    } else {
        return 1;
    }

}

function dameEquivalencia(grupo, u1, u2) {

    return unidades[grupo].equivalencias[u1][u2];
}

function dameGrupo(unidad) {
    for (var x in unidades) {
        if (unidades[x].unidades.indexOf(unidad) !== -1) {
            return unidades[x].grupo;
        }
    }
    return false;
}

function escribeError(mensaje) {
    $('.ocultable').hide();
    $resultado
        .removeClass('alert-success')
        .addClass('alert-danger');
    $resultado.html('<h5>' + mensaje + '</h5>');
}

function escribeResultado(numero, u1, u2, resultado) {
    var primeraUnidadFinal = (numero === 1) ? u1 : u1 + 's';
    var cadenaSegundaUnidad = (resultado === 1) ? u2 : u2 + 's';
    var cadenaEquivalencia = (numero === 1) ? " equivale " : " equivalen ";
    var mensaje = '<h5>'
        + '<strong>' + numero + '</strong>'
        + ' '
        + primeraUnidadFinal
        + cadenaEquivalencia + 'a '
        + '<strong>' + resultado + '</strong>'
        + ' '
        + cadenaSegundaUnidad
        + '</h5>';
    $resultado
        .removeClass('alert-danger')
        .addClass('alert-success');
    $resultado.html(mensaje);
    $('.ocultable').show();
}

function escuchaElementos() {
    $selectGrupoUnidades.change(function () {
        llenaPrimerGrupo($selectPrimerGrupo, $(this).val());
        llenaSegundoGrupo($selectSegundoGrupo, $(this).val());
        $inputPrimerGrupo.val(1);
        $inputPrimerGrupo.keyup();
        convierte(
            $(this).val(),
            $(this).val(),
            $inputPrimerGrupo.val(),
            $selectPrimerGrupo.val(),
            $selectSegundoGrupo.val(), false);
    });
    $selectPrimerGrupo.change(function () {
        convierte(
            $selectGrupoUnidades.val(),
            $selectGrupoUnidades.val(),
            $inputPrimerGrupo.val(),
            $(this).val(),
            $selectSegundoGrupo.val(), false);
    });
    $selectSegundoGrupo.change(function () {
        convierte(
            $selectGrupoUnidades.val(),
            $selectGrupoUnidades.val(),
            $inputPrimerGrupo.val(),
            $selectPrimerGrupo.val(),
            $(this).val(), false);
    });
    $inputPrimerGrupo.keyup(function () {
        if (!isNaN($(this).val())) {
            if ($(this).val()) {
                if ($(this).val().length <= limiteLongitudNumeros - 1) {
                    convierte(
                        $selectGrupoUnidades.val(),
                        $selectGrupoUnidades.val(),
                        $(this).val(),
                        $selectPrimerGrupo.val(),
                        $selectSegundoGrupo.val(), false);
                } else {
                    escribeError('Lo siento, solamente se permiten números de <strong>16 cifras</strong> sin incluir el punto decimal');

                    $('.ocultable').show();
                }
            }
        } else {
            escribeError('Ingresa un número en la caja de texto número 1');
            $('.ocultable').show();
        }

    });
    $inputSegundoGrupo.keyup(function () {
        if (!isNaN($(this).val())) {
            if ($(this).val()) {
                if ($(this).val().length <= limiteLongitudNumeros - 1) {
                    convierte(
                        $selectGrupoUnidades.val(),
                        $selectGrupoUnidades.val(),
                        $(this).val(),
                        $selectSegundoGrupo.val(),
                        $selectPrimerGrupo.val(), true);
                } else {
                    escribeError('Lo siento, solamente se permiten números de <strong>16 cifras</strong> sin incluir el punto decimal');
                    $('.ocultable').show();
                }
            }
        } else {
            escribeError('Ingresa un número en la caja de texto número 2');
            $('.ocultable').show();
        }

    });
    $btnAcercaDe.click(function () {
        $ventanaAcercaDe.modal('show');
    });
    $inputPrincipal.keyup(function () {
        compruebaValores($(this).val());
    });
    $btnEjemplo.click(function () {

        $('div#manual').modal('show');
    });
}

function grados(grupo, u1, u2, numero) {
    if (grupo === "Temperatura") {
        switch (u1) {
            case "grado fahrenheit":
                switch (u2) {
                    case "grado fahrenheit":
                        return numero;
                        break;
                    case "grado celsius":
                        return (numero - 32) * (5 / 9);
                        break;
                    case "kelvin":
                        return ((5 * (numero - 32)) / 9) + 273.15;
                        break;
                }
                break;
            case "grado celsius":
                switch (u2) {
                    case "grado fahrenheit":
                        return 32 + ((9 / 5) * numero );
                        break;
                    case "grado celsius":
                        return numero;
                        break;
                    case "kelvin":
                        return numero + 273.15;
                        break;
                }
                break;
            case "kelvin":
                switch (u2) {
                    case "grado fahrenheit":
                        return ((9 * (numero - 273.15)) / 5) + 32;
                        break;
                    case "grado celsius":
                        return numero - 273.15;
                        break;
                    case "kelvin":
                        return numero;
                        break;
                }
                break;
        }
    }
}

function llenaPrimerGrupo(selector, grupo) {
    selector.empty();
    for (var x in unidades[grupo].unidades) {
        selector.append($('<option>', {
            value: unidades[grupo].unidades[x],
            text: aLetraCapital(unidades[grupo].unidades[x])
        }));
    }
}

function llenaSegundoGrupo(selector, grupo) {
    selector.empty();
    for (var x in unidades[grupo].unidades) {
        selector.append($('<option>', {
            value: unidades[grupo].unidades[x],
            text: aLetraCapital(unidades[grupo].unidades[x])
        }));
    }
}

function llenaSelectUnidades(selector) {
    for (var opcion in unidades) {
        selector.append($('<option>', {
            value: opcion,
            text: opcion
        }));
    }
}

function principal() {
    $inputPrincipal.focus();
    setInterval(cambiaPlaceholder, segundosCambiaPlaceholder * 1000);
    llenaSelectUnidades($selectGrupoUnidades);
    escuchaElementos();
    $('.ocultable').hide();
}

Poniendo todo junto

Todo este conversor está hecho con Bootstrap (tanto estilos como funcionamiento de modals) y jQuery. Sé que jQuery ya casi no se usa pero recuerda que este programa fue hecho hace 5 años y apenas lo he publicado.

Si quieres el código fuente completo lo dejo en GitHub. También puedes acceder a una demostración aquí.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *