javascript

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

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.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

3 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

3 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

3 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

3 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

3 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

4 días hace

Esta web usa cookies.