1

I have many input fields (3 on each row), that basically do the same thing:

Input1 * Input2 = DisabledInput=result

Input3 * Input4 = DisabledInput=result

...

What would be the best way to multiply input fields on each row with js?

Doing it with getElementsById works, but that means creating several dozen IDs, even though the logic on each row is the same.

<div class="row">
<input class="input first" id="id1" type="text" name="first" oninput="calculate()" value="" placeholder="Enter number">
<input class="input second" id="id2" type="text" name="second" oninput="calculate()" value="" placeholder="Enter number">
<input class="input result" id="idResult" type="text" name="result" oninput="calculate()" value="" placeholder="Enter number">
</div>
 function calculate() {
        var first = document.getElementById('id1').value;   
        var second = document.getElementById('id2').value;
        var result = document.getElementById('idResult');   
        var finalResult = id1 * id2;
        idResult.value = Math.round(finalResult * 100) / 100;
        }

I tried using

var get = function(clsName) {
          return document.getElementsByClassName(clsName)[0];
        };

get('result').innerHTML = +get('first').innerHTML * +get('second').innerHTML;

But it's not working. I'm just wondering what would the best approach be to problems like this? Surely not entering 50 different Ids for doing the same thing?

prmz
  • 311
  • 1
  • 3
  • 13
  • Possible duplicate of [How to correctly iterate through getElementsByClassName](https://stackoverflow.com/questions/15843581/how-to-correctly-iterate-through-getelementsbyclassname) – devlin carnate Jun 07 '19 at 16:19
  • Not sure if this is true, but I think there is a way you could get the values from the "superclass" of the form and get it's children's (input) values and use them. – Compiler v2 Jun 07 '19 at 16:28
  • inputs use value, not innerHTML – epascarello Jun 07 '19 at 17:46

2 Answers2

1

Update

New Solution

Using .class Attribute
See Demo 2.
If [name] is too busy of an attribute and you are concerned with possible conflicts, .class can be just as effective with this particular solution. In Demo 2 all [name] attributes are now .class attributes. The .className property is used instead of .name property. If any of your targeted elements have more than one class then use .classList.contains() property and method.

Original Solution

Using [name] Attribute
See Demo 1.
As long as your layout pattern is the same consistently

<input...> <input...> <output...> 

You can have an unlimited number of input/output combos that operate independently from one another by delegating an event. The demo provided has a form tag that manages all form controls nested within it when the input event is triggered. An input event occurs whenever a form control gets data from a user by typing or selecting. The terse syntax used to reference the form tag (see first comment in demo) is from the HTMLFormElement interface. Also, instead of another input for the result, use an output tag instead, and use type='number' on inputs which will facilitate correct input. Details are commented in demo.

Note: I intentionally left that expression out of the function intentionally (it looked excessive to me since context or an explanation wasn't provided about said expression). If you still want to use it just replace line F with the following:

let product = origin.valueAsNumber * B.valueAsNumber;
C.value = Math.round(product * 100) / 100;

Do the same for line X and replace B.valueAsNumber; with A.valueAsNumber;

Demo 1

/* 
Register the first form on the page to the input event
When any input within the form gets user data call function multiply()
*/
document.forms[0].oninput = multiply;

/** multiply()
//A - Pass Event Object
//B - Find input getting the user data - e.target always points to
      the input getting data or button getting clicked, etc. 
//C - if origin's [name=factorA]...
//D - ...then B is the tag after origin...
//E - ...and C is the tag after B...
//F - ...Set the value of C as the product of A and B
      All form control values are strings not numbers, so once a
      value is extracted from a form control it must be converted
      to a number -- input.valueAsNumber is one of several ways
      to do so.
- The second control statement handles input event if origin is
[name=factorB]
*/
function multiply(e) { //A
  const origin = e.target; //B
  if (origin.name === 'factorA') { //C
    let B = origin.nextElementSibling; //D
    let C = B.nextElementSibling; //E
    C.value = origin.valueAsNumber * B.valueAsNumber; //F
  } else if (origin.name === 'factorB') {
    let A = origin.previousElementSibling;
    let C = origin.nextElementSibling;
    C.value = origin.valueAsNumber * A.valueAsNumber; //X
  } else {
    return false;
  }
}
:root {
  font: 700 3vw/1.2 Consolas
}

input,
output,
label {
  display: inline-block;
  font: inherit;
  text-align: right;
}

input {
  width: 30vw
}
<form>
  <fieldset>
    <legend>Multiplication</legend>
    <label>
    <input name='factorA' type='number' value='0'> &times; <input name='factorB' type='number' value='0'> &equals; <output name='product'>0</output>
    </label><br>
    <label>
    <input name='factorA' type='number' value='0'> &times; <input name='factorB' type='number' value='0'> &equals; <output name='product'>0</output>
    </label><br>
    <label>
    <input name='factorA' type='number' value='0'> &times; <input name='factorB' type='number' value='0'> &equals; <output name='product'>0</output>
    </label><br>
  </fieldset>
</form>

Demo 2

/* 
Register the first form on the page to the input event
When any input within the form gets user data call function multiply()
*/
document.forms[0].oninput = multiply;

/** multiply()
//A - Pass Event Object
//B - Find input getting the user data - e.target always points to
      the input getting data or button getting clicked, etc. 
//C - if origin's .factorA...
//D - ...then B is the tag after origin...
//E - ...and C is the tag after B...
//F - ...Set the value of C as the product of A and B
      All form control values are strings not numbers, so once a
      value is extracted from a form control it must be converted
      to a number -- input.valueAsNumber is one of several ways
      to do so.
- The second control statement handles input event if origin is
.factorB
*/
function multiply(e) { //A
  const origin = e.target; //B
  if (origin.className === 'factorA') { //C
    let B = origin.nextElementSibling; //D
    let C = B.nextElementSibling; //E
    C.value = origin.valueAsNumber * B.valueAsNumber; //F
  } else if (origin.className === 'factorB') {
    let A = origin.previousElementSibling;
    let C = origin.nextElementSibling;
    C.value = origin.valueAsNumber * A.valueAsNumber; //X
  } else {
    return false;
  }
}
:root {
  font: 700 3vw/1.2 Consolas
}

input,
output,
label {
  display: inline-block;
  font: inherit;
  text-align: right;
}

input {
  width: 30vw
}
<form>
  <fieldset>
    <legend>Multiplication</legend>
    <label>
    <input class='factorA' type='number' value='0'> &times; <input class='factorB' type='number' value='0'> &equals; <output class='product'>0</output>
    </label><br>
    <label>
    <input class='factorA' type='number' value='0'> &times; <input class='factorB' type='number' value='0'> &equals; <output class='product'>0</output>
    </label><br>
    <label>
    <input class='factorA' type='number' value='0'> &times; <input class='factorB' type='number' value='0'> &equals; <output class='product'>0</output>
    </label><br>
  </fieldset>
</form>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • This works, however ideally I would want to avoid using "name" because the site is in php and we need to target those input "names". That is why I thought going with getElementsByClassName would be the way to go. I will accept your answer, however, because it works and I didn't make myself clear enough in my initial question. – prmz Jun 09 '19 at 08:21
  • 1
    @prima instead of name use class. See update. You see with my simple solution and your consistent layout of `input` + `input` + `output` you don't need to use a collection like `.getElementsByClassName()` (btw don't use that dinosaur use `.querySelectorAll(".class")` instead) – zer00ne Jun 09 '19 at 09:19
  • I would just put one comment here for someone who might want to do something similar. This does work if you use a "clean style" of . However, once you want to style this with a bootstrap or something else and use column classes before inputs, for example, it won't work. input for example. – prmz Jun 27 '19 at 11:11
  • @prima I agree, one needs to take care when using BootStrap predefined classes with any CSS/HTML -- custom or otherwise. The specificity of BS classes are very high making them very difficult to override. However I disagree with the syntax and use of ``. It is a very specialized tag that's allowed only to be placed as a direct descendant (child tag) of a ``. It's purpose is to apply limited set of styles to a column of `. Also `` is a hidden type and a void element which means no end tag `` which also means it cannot contain any content. – zer00ne Jun 27 '19 at 13:34
  • You are right. It was a typo. Bootstrap 4 uses `
    ` for columns. So I'd imagine it would be hard targeting inputs if each `input` is wrapped inside the `.col` class.
    – prmz Jun 28 '19 at 12:49
0

With getElementsByClassName

total = 0
for (instance of document.getElementsByClassName(clsName)) {
    total+=parseInt(instance.value)
}
console.log(total)

An example for your problem

total = 0
i = 1
for (instance of document.getElementsByClassName(clsName)) {
    if (i%3!=0) {
        total+=parseInt(instance.value)
        i++
    } else {
        instance.value = total
        total = 0
        i = 1
    }
}

With getElementById

ids = ['id1', 'id2', 'idResult']
for (let id of ids) {
  console.log(document.getElementById(id).value)
}
Lucas Vazquez
  • 1,456
  • 16
  • 20
  • How would you do the calculation after you iterated through the elements? What's next? Would you store the data to another array/variables and get the elements through that and then do the computation? It seems a little bit counter-intuitive. – Compiler v2 Jun 07 '19 at 16:34
  • That's the importance of define a context I dont know exactly what you want to do. Check the new example I leave. – Lucas Vazquez Jun 07 '19 at 20:34