1

I'm trying to create 6 textboxes made from , top 3 serve as input, bottom 3 serve as the output. Three input textboxes read 3 numbers. When the Calculate button is clicked, the maximum, minimum, and average of 3 input numbers should be calculated and displayed in the textbox with corresponding label. Is there a reason why my code doesn't work?

function maximum() //find the max
{
  var max;
  if (document.getElementsByTagName("input")[0].value > document.getElementsByTagName("input")[1].value && document.getElementsByTagName("input")[0].value > document.getElementsByTagName("input")[2].value) {
    max = document.getElementsByTagName("input")[0].value;
  } else if (document.getElementsByTagName("input")[1].value > document.getElementsByTagName("input")[0].value && document.getElementsByTagName("input")[1].value > document.getElementsByTagName("input")[2].value) {
    max = document.getElementsByTagName("input")[1].value;
  } else {
    max = document.getElementsByTagName("input")[2].value;
  }
  document.getElementsByTagName("output")[0] = max;

  //alert(maximumDemo.innerHTML);
}

function average() //find the average
{
  var total = 3;
  var i, aver;
  aver = (document.getElementsByTagName("input")[0].value + (document.getElementsByTagName("input")[1].value + (document.getElementsByTagName("input")[2].value))) / total;
  document.getElementsByTagName("output")[1] = aver;
}

function minimum() {
  if (document.getElementsByTagName("input")[0].value < document.getElementsByTagName("input")[1].value && document.getElementsByTagName("input")[0].value < document.getElementsByTagName("input")[2].value) {
    min = document.getElementsByTagName("input")[0].value;
  } else if (document.getElementsByTagName("input")[1].value < document.getElementsByTagName("input")[0].value && document.getElementsByTagName("input")[1].value < document.getElementsByTagName("input")[2].value) {
    min = document.getElementsByTagName("input")[1].value;
  } else {
    min = document.getElementsByTagName("input")[2].value;
  }
  document.getElementsByTagName("output")[2] = min; //find the minimum

  //alert(minimumDemo.innerHTML);
}
<form>
  Number 1: <input type="text" name="num1"> <br> Number 2: <input type="text" name="num2"> <br> Number 3: <input type="text" name="num3"> <br> Maximum: <output type="text" name="max"></output> <br>
  <!--preferably in textbox form-->
  Average: <output type="text" name="avg"></output> <br>
  <!--preferably in textbox form-->
  Minimum: <output type="text" name="min"></output> <br>
  <!--preferably in textbox form-->
  <br><br>
</form>
<button onCLick="maximum();average();minimum();">Calculate</button>
newbs
  • 73
  • 2
  • 7
  • 1
    *bottom 3 serve as output* Don't use an input for output. Use a normal element and just populate its `.textContent`. – Scott Marcus Mar 24 '18 at 03:11
  • I'm having a hard time seeing that, is it possible if you could show me. Feel like I'm blind not being able to see this. Please and thank you. – newbs Mar 24 '18 at 03:15

3 Answers3

8

You have several problems, but the biggest was that you were attempting to set your output elements themselves to the answers to your math problems instead of setting the .textContent of your elements to the answers to your math.

Now, since you are just learning all of this, let's break some bad habits before they become set in stone. No doubt you've copied much of your code from what you've seen others use and, unfortunately, most of the code on the web gets created this way and not by people who really understand what they are writing.

With this in mind...

Do not set up event handlers via HTML event attributes (i.e. onclick, onmouseover, etc.). This is a 25+ year old technique that we used before we had modern standards and best practices and because it's easy to use, people keep using it. But there are a variety of reasons why you should not use this technique and instead separate your JavaScript from your HTML. Instead, keep your JavaScript separate and use .addEventListener() to hook up your elements to their respective callback functions.

Just get your element references once, not every time you run your functions because scanning the document for an element takes time and resources. There's no reason to keep scanning for the same element over and over.

Related to that. Be careful with .getElementsByTagName() as it returns a "live node list", which means that it has to re-scan the document to ensure that it keeps its list up to date. This can be extremely wasteful in terms of performance. Instead, .getElementById(), .querySelector() and .querySelectorAll() should probably be the most common ways to scan for individual elements or groups of elements.

The output element doesn't have an HTML type attribute. input and button elements allow for setting the type. output is purely a semantic element, and for all intents and purposes, not that much functionally different from a span element. It's just a way for the browser to know that it will contain something that the page designer deems as some kind of output, from anywhere. It's also not supported in any desktop version of IE (if that matters to you). It's fine that you are using it, but I think you misunderstand what it's for an how it's used.

You really don't need any if/then code in your solution because JavaScript has a built-in Math object, which has .max and .min methods that do exactly what you are attempting.

Remember that the "T" in HTML stands for "Text". No matter what you think you are entering into a form field, it is processed as text and when JavaScript obtains that value, it processes it as text as well. And, in JavaScript the + operator can mean string concatenation or it can mean mathematical addition. Which one it does depends on what the operands are. If one of the operands is a string then string concatenation is done. So, you need to convert the data in your input elements to numbers before you do any math on them. This can be done in a variety of ways (implicit and explicit), but one simple way is to prepend a + operator in front of a string that is convertible to a number and you'll see that in my average code below.

Lastly (and this wasn't causing your code to not work, but you should know), every HTML document must have a <head> section just prior to the <body> and the <head> section must have a non-empty <title> element within it for the document to be considered valid. Browsers don't validate HTML, they simply skip HTML that they don't understand. This means that you could very well have a web page that "works" for you in your browser, but is technically incorrect. Always validate your HTML at http://validator.w3.org to ensure it's correct.

<!doctype html>
<html>
<head>
  <title>Basic JavaScript 101</title>
</head>
<body>
 
<form>
  Number 1: <input type="text" name="num1"> <br>
  Number 2: <input type="text" name="num2"> <br>
  Number 3: <input type="text" name="num3"> <br>
 
  Maximum: <output id="max"></output> <br> <!-- output should not be in input type elements  -->
  Average: <output id="avg"></output> <br> <!-- output should not be in input type elements  -->
  Minimum: <output id="min"></output> <br> <!-- output should not be in input type elements  -->
  <br><br>
</form>  
<button type="button" id="calc">Calculate</button>

<script>
  // Get references to the elements just once
  var num1 = document.querySelector("[name='num1']");
  var num2 = document.querySelector("[name='num2']");
  var num3 = document.querySelector("[name='num3']"); 

  var max = document.getElementById("max");
  var avg = document.getElementById("avg");
  var min = document.getElementById("min"); 
  
  var btn = document.getElementById("calc");
  
  // Set up your event handler(s) in JavaScript, not with HTML attributes
  btn.addEventListener("click", function(){
    maximum();
    average();
    minimum();
  });

  function maximum() {
    // You can't just set an element to a value. You have to set the content of the 
    // element to a value. Also, JavaScript provides a built-in Math object that
    // can get you the maximum number from a set of numbers. No if/then needed.
    max.textContent = Math.max(num1.value, num2.value, num3.value);  
  }
  
  function average() {
    // Convert values to numbers and do math
    avg.textContent = (+num1.value + +num1.value + +num3.value) / 3
  }
 
  function minimum() {
    // You can't just set an element to a value. You have to set the content of the 
    // element to a value. Math can also get you the minimum number in a set:
    min.textContent = Math.min(num1.value, num2.value, num3.value);  
  }
</script>
</body>
</html>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • 1
    Good answer, but you are wrong about [output](https://html.spec.whatwg.org/multipage/form-elements.html#the-output-element). It has `value`, `defaultValue`, and some other form-related attributes that a span-element doesn't have. It also have `type` (but it is read only, and returns `output`). – some Mar 24 '18 at 13:50
  • @some Also, `type`, `value` and `defaultvalue` are parts of the DOM API interface and usable as properties, but not as actual attributes in the static HTML. In both the WHATWG and W3C standards it clearly shows that only `form`, `for` and `name` are valid HTML attributes. When they use the term `attribute` in relation to `type`, `value` and `defaultValue`, they are referring to the DOM Interface, not HTML. If you try to validate ``, it will fail. – Scott Marcus Mar 24 '18 at 16:36
0

Use <div>instead of <br> for a block. Add an idto every <output>to access it with document.getElementById().

What, if you would like to add an input?

Therefore access all inputs with document.querySelectorAll("input[name^='nums']"). Now you can add an input, like <input type="text" name="num4">and so on. Use this to handle all of the calculations.

Example

var max = document.getElementById("max"),
  avg = document.getElementById("avg"),
  min = document.getElementById("min"),
  result = document.getElementById("result");

result.addEventListener("click", function(e) {
  e.preventDefault();
  var inputs = document.querySelectorAll("input[name^='num']"),
    numbers = [],
    sum = 0,
    l = inputs.length;
  for (var i = 0; i < l; i += 1) {
    var n = +inputs[i].value;
    numbers.push(n);
    total += n;
  }
  max.textContent = Math.max(...numbers);
  avg.textContent = (total / l).toFixed(2);
  min.textContent = Math.min(...numbers);
});
<form>
  <div><label>Number 1:</label><input type="text" name="num1"></div>
  <div><label>Number 2:</label><input type="text" name="num2"></div>
  <div><label>Number 3:</label><input type="text" name="num3"></div>
  <div>
    <label>Maximum:</label><output type="text" name="max" id="max"></output>
  </div>
  <div><label>Average:</label><output type="text" name="avg" id="avg"></output></div>
  <div>
    <label>Minimum:</label>
    <output type="text" name="min" id="min"></output>
  </div>
  <button id="result">Calculate</button>
</form>
Community
  • 1
  • 1
  • `div` creates a new block. `br` moves down a line in the current block. Since all 3 inputs are related, it is perfectly appropriate to use `br`. – Scott Marcus Mar 24 '18 at 17:43
0

This answer is meant to supplement Scott Marcus's answer. He makes some good points and provides a detailed explanation but I think the program demonstration can be improved significantly -

const f = document.querySelector("form") // <-- single DOM query

const max = Math.max                       //
const min = Math.min                       // <-- functions take args
const avg = (a, b, c) => (a + b + c) / 3   //

const calc = function()
{ const x = Number(f.num1.value) || 0 //
  const y = Number(f.num2.value) || 0 // <-- parsing w/ Number
  const z = Number(f.num3.value) || 0 //
  f.max.value = max(x, y, z) //
  f.avg.value = avg(x, y, z) // <-- functions return values 
  f.min.value = min(x, y, z) //
}

f.calc.addEventListener("click", calc)
<form>
  Number 1: <input type="number" name="num1"><br>
  Number 2: <input type="number" name="num2"><br>
  Number 3: <input type="number" name="num3"><br>
  <br>
  Maximum: <output name="max"></output><br>
  Average: <output name="avg"></output><br>
  Minimum: <output name="min"></output><br>
  <br>
  <button type="button" name="calc">Calculate</button>
</form>

NB we can reference form child elements directly by their name attribute, such as f.num1 or f.min. Notice how id attributes are not even declared. This also saves us from having to write document.querySelector for each input and output.

Run the snippet to see how it behaves. Here are some additional refinements -

const max = Math.max
const min = Math.min
const avg = (a, b, c) => (a + b + c) / 3

const getNumber = (elem) => Number(elem.value) || 0 // <-- make functions to
const set = (elem, x) => elem.value = x             // <--  avoid repetition

const calc = function(form) // form is an input, no longer global
{ const x = getNumber(form.num1) //
  const y = getNumber(form.num2) // <-- use getNumber helper
  const z = getNumber(form.num3) // 
  set(form.max, max(x, y, z)) //
  set(form.avg, avg(x, y, z)) // <-- use set helper
  set(form.min, min(x, y, z)) //
}

const f = document.querySelector("form") // <-- single DOM query

f.addEventListener("input", event => calc(f)) // <-- listen to input event
<form>
  Number 1: <input type="number" name="num1"><br>
  Number 2: <input type="number" name="num2"><br>
  Number 3: <input type="number" name="num3"><br>
  <br>
  Maximum: <output name="max"></output><br>
  Average: <output name="avg"></output><br>
  Minimum: <output name="min"></output><br>
</form>
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Using the `form.elementName` or just plain `elementID` techniques for referencing DOM elements is considered legacy and should not be advocated as they don't conform to the modern DOM API standard. – Scott Marcus Oct 02 '19 at 20:51