通过下拉列表以及用户输入和JavaScript将英制值转换为公制

时间:2018-10-31 14:27:27

标签: javascript

我有这些变量:

  var imperialConverterTable = {
    inch: 25.4,                 
    foot: 12*25.4,           
    yard: 3*12*25.4,    
    mile: 1760*3*12*25.4
  };
  var metricConverterTable = {
    millimeter: 1, 
    centimeter: 10,  
    decimeter:  100, 
    meter:      1000,  
    kilometer:  1E6,  
    mil:        1E7      
  };

我想设置一个JavaScript,该JavaScript可在用户输入时立即更改值。理想情况下,它还会根据用户在两个不同的下拉列表中做出的选择来更改值:

<input type="number" id="lengthVal" value=«10»>
    <select id="imperialBenevning">
    <option id="inch" type="number" oninput="imperialConverterTable(this.id,this.value)" onchange="imperialConverterTable(this.id,this.value)">inch</option>
    <option id="foot" type="number" oninput="imperialConverterTable(this.id,this.value)" onchange="imperialConverterTable(this.id,this.value)">foot</option>
    <option id="yard" type="number" oninput="imperialConverterTable(this.id,this.value)" onchange="imperialConverterTable(this.id,this.value)">yard</option>
    <option id="mile" type="number" oninput="imperialConverterTable(this.id,this.value)" onchange="imperialConverterTable(this.id,this.value)">mile</option>
    </select>

    <label> = </label>

    <select id="metricBenevning">
// Some kind of output-field here?
    <option id="millimeter" type="number" oninput="metricConverterTable(this.id,this.value)" onchange="metricConverterTable(this.id,this.value)">millimeter</option>
    <option id="centimeter" type="number" oninput="metricConverterTable(this.id,this.value)" onchange="metricConverterTable(this.id,this.value)">centimeter</option>
    <option id="decimeter" type="number" oninput="metricConverterTable(this.id,this.value)" onchange="metricConverterTable(this.id,this.value)">decimeter</option>
    <option id="meter" type="number" oninput="metricConverterTable(this.id,this.value)" onchange="metricConverterTable(this.id,this.value)">meter</option>
    <option id="kilometer" type="number" oninput="metricConverterTable(this.id,this.value)" onchange="metricConverterTable(this.id,this.value)">kilometer</option>
    <option id="mil" type="number" oninput="metricConverterTable(this.id,this.value)" onchange="metricConverterTable(this.id,this.value)">mil</option>
    </select>

我试图制作这样的if函数:

function lengthVal(source,valNum) {

  valNum = parseFloat(valNum);
  var foot = document.getElementById("foot");
  var inch = document.getElementById("inch");
  var yard = document.getElementById("yard");
  var mile = document.getElementById("mile");
  var centimeter = document.getElementById("centimeter");
  var decimeter = document.getElementById("decimeter");
  var meter = document.getElementById("meter");
  var kilometer = document.getElementById("kilometer");
  var mil = document.getElementById("mil");

  if (source=="foot") {
    meter.value=(valNum/3.2808).toFixed(2);
    inch.value=(valNum*12).toFixed(2);
    //and so on for all the values, but no luck so far..
  }

我想一下这几个问题,但是只有一些提示或评论会很有帮助。

1 个答案:

答案 0 :(得分:1)

像这样吗?

// JS' Numeric system sometimes has some issues that can not be properly represented in binary format
const fixFloat = value => Math.round(value * 1e10) / 1e10;

// a simple definition of the units, imo. very readable
// I made the meter = 1 since in the metric system 
// all other lengths are derived from that. It's literally in the name
// milli-meter == one thousandth meter
// this only affects the values, but not the conversions
const meter = 1,
  decimeter = meter / 10,
  centimeter = meter / 100,
  millimeter = meter / 1000,
  kilometer = meter * 1000,
  mil = 10 * kilometer, // Scandinavian mile, had to look this one up, imo. it's not common

  inch = 25.4 * millimeter,
  foot = 12 * inch,
  yard = 3 * foot,
  mile = 1760 * yard;

// Since I have them defined now, 
// I want to create the options for the prepared select-boxes from these values 

let options = [
    ["Imperial", {
      inch,
      foot,
      yard,
      mile
    }],

    ["Metric", {
      millimeter,
      centimeter,
      decimeter,
      meter,
      kilometer,
      mil
    }]
  ].map(([label, units]) => `
<optgroup label="${label}">
  ${ Object.entries(units)
           .sort((a,b) => a[1] - b[1])
           .map(([unit, value]) => `<option value="${fixFloat(value)}">${unit}</option>`)
           .join("\n  ")
  }
</optgroup>`)
  .join("")
  .trim();

console.log(options);

[...document.querySelectorAll('#aa, #bb')].forEach(select => {
  select.innerHTML = options;
});

//now let's bring some interactivity to this:

function convert(value, fromScale, toScale) {
  return fixFloat(value * fromScale / toScale);
}

function update(e) {
  // deciding in wich direction to compute, based on what field has been changed
  switch (e.target.id) {
    // -->
    case "a":
    case "bb":
      document.querySelector("#b").value = convert(
        document.querySelector("#a").value,
        document.querySelector("#aa").value,
        document.querySelector("#bb").value
      );
      break;
      
    // <--
    case "b":
    case "aa":
      document.querySelector("#a").value = convert(
        document.querySelector("#b").value,
        document.querySelector("#bb").value,
        document.querySelector("#aa").value
      );
      break;
  }
}

// add the event-listener to the input and select-boxes
[...document.querySelectorAll('select, input')].forEach(elm => {
  elm.oninput = elm.onchange = update;
});
<input type="number" id="a" value="1">
<select id="aa"></select>

<span>&nbsp;=&nbsp;</span>

<input type="number" id="b" value="1" >
<select id="bb"></select>

由于该代码的上半部分仅用于定义创建<option>的单位,因此您可以直接在<select>框中编写生成的标记,而JS只需要处理计算即可:

// JS' Numeric system has some issues with numbers that 
// can not be properly represented in binary format:
// check: console.log(0.1 * 0.1);
const fixFloat = value => Math.round(value * 1e10) / 1e10;

function convert(value, fromScale, toScale) {
  return fixFloat(value * fromScale / toScale);
}

function update(e) {
  // deciding in wich direction to compute, based on what field has been changed
  switch (e.target.id) {
    // -->
    case "a":
    case "bb":
      document.querySelector("#b").value = convert(
        document.querySelector("#a").value,
        document.querySelector("#aa").value,
        document.querySelector("#bb").value
      );
      break;

      // <--
    case "b":
    case "aa":
      document.querySelector("#a").value = convert(
        document.querySelector("#b").value,
        document.querySelector("#bb").value,
        document.querySelector("#aa").value
      );
      break;
  }
}

// add the event-listener to the input and select-boxes
[...document.querySelectorAll('select, input')].forEach(elm => {
  elm.oninput = elm.onchange = update;
});
<input type="number" id="a" value="1">
<select id="aa">
  <!-- 
    the unit for these values is meter
    1 inch === 0.0254 meter
  -->
  <optgroup label="Imperial">
    <option value="0.0254">inch</option>
    <option value="0.3048">foot</option>
    <option value="0.9144">yard</option>
    <option value="1609.344">mile</option>
  </optgroup>
  <optgroup label="Metric">
    <option value="0.001">millimeter</option>
    <option value="0.01">centimeter</option>
    <option value="0.1">decimeter</option>
    <option value="1">meter</option>
    <option value="1000">kilometer</option>
    <option value="10000">mil</option>
  </optgroup>
</select>

<span>&nbsp;=&nbsp;</span>

<input type="number" id="b" value="1">
<select id="bb">
  <optgroup label="Imperial">
    <option value="0.0254">inch</option>
    <option value="0.3048">foot</option>
    <option value="0.9144">yard</option>
    <option value="1609.344">mile</option>
  </optgroup>
  <optgroup label="Metric">
    <option value="0.001">millimeter</option>
    <option value="0.01">centimeter</option>
    <option value="0.1">decimeter</option>
    <option value="1">meter</option>
    <option value="1000">kilometer</option>
    <option value="10000">mil</option>
  </optgroup>
</select>