0

I'm trying to make a small calculator with input elements. I've built a plus button to insert a new input field after the last one. It's working.

Now I'm trying to sum each input field. But I get undefined in the console. I don't know what's wrong because I'm new to programming. I tried to get the value of the input fields with class .input-time and then making it into an array. And then doing a for loop to sum them.

EDIT:

Thanks at all for the help. I used @Yoiki's code because it's a new and easy way. I edited the code snipped. It's working now.

$(function() {
  
  // ADDING NEW INPUT FIELD
  $('.button-plus').on('click', function(event) {
    event.preventDefault();
    
    let inPut = $('.input-time');
    let inPutArr = jQuery.makeArray( inPut );
    // console.log(inPutArr);

    let lastInPut = inPutArr[inPutArr.length-1]; // get last elem of array
    $(lastInPut).addClass('lastone'); // add class to it
    $('.lastone').after('<input type=\"text\" class=\"input-time\">');
    $('.lastone').delay(10).removeClass('lastone');
  });
  
  /* OLD CODE - NOT WORKING!!!
  $('#button-calc').on('click', function(event) {
    event.preventDefault();
    let inPutV = $('.input-time').val();
    let inPutVal = jQuery.makeArray( inPutV );
    for (var i = 0; i < inPutVal.length; i++) {
      var sum = sum + inPutVal[i];
    }
    console.log(sum);
  });
  */
  
  // NEW CODE - WORKING!!!
  $('#button-calc').on('click', function(event) {
    event.preventDefault();

    let sum = 0;

    $('.input-time').each (function () {
      sum += +$(this).val();
    });
    console.log (sum);
  });
});
/* IGNORE THE STYLE ;) */

input {
  width: 160px;
  height: 20px;
  padding: 10px 20px 10px 20px;
  border: none;
  border-radius: 20px;
  font-size: 15px;
  display: block;
  margin-bottom: 10px;
}

.container {
  background-color: #dde4fe;
  padding: 20px;
  width: 200px;
  margin-right: 20px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 100px;
}

.button-plus {
  width: 40px;
  height: 40px;
  background-color: #9aadfd;
  color: #fff;
  cursor: pointer;
  margin-top: 10px;
  margin-left: auto;
  margin-right: auto;
  border-radius: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}


#button-calc {
  width: 200px;
  height: 40px;
  background-color: #5677fc;
  color: #fff;
  cursor: pointer;
  margin-top: 20px;
  margin-bottom: 0;
}

.btn-plus {
  width: 20px;
  margin-left: auto;
  margin-right: auto;
  height: 5px;
  background-color: #5677fc;
  border-radius: 5px;
  position: absolute;
}

.plus-1 {
  transition: 0.5s;
  transform: rotate(0deg);
}

.plus-2 {
  transition: 0.5s;
  transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<section class="container">
  <div class="container-inner">
    <input type="text" class="input-time">
    <input type="text" class="input-time">
    <div class="button-plus">
      <div class="btn-plus plus-1"></div>
      <div class="btn-plus plus-2"></div>
    </div>
    <input type="submit" id="button-calc" value="Calculate">
  </div>
</section>

6 Answers 6

1

sum is undefined, so when you add a number to it, you're adding that number to undefined. You need to set sum = 0 first.

A suggestion for helping avoid accidentally using undefined values, try including 'use strict' at the beginning of your code -- it forces you to define your variables, among other things.

Sign up to request clarification or add additional context in comments.

Comments

1

Do not use val() when you are gathering objects.

inPutV = $('.input-time');

But use val() when extracting values:

sum = sum + inPutVal[i].val()

Comments

0

What you want to do is iterating all element with class input-time and get value for each element. You cannot get directly an array of values.

$('#button-calc').on('click', function(event) {
   event.preventDefault();

   let sum = 0; // Init the variable outside the loop

   $('.input-time').each (function () { // This is how you iterate all .input-time element
    sum += +$(this).val(); // And then you get the value
   });
   console.log (sum);
});

$(this).val() return the text of the input but it is a string and you need a number to add to sum. That's why i use a + before $(this).val() to transform a string in number.

You should also check if .input-time element is a string to avoid strange behavior.

Comments

0

You are trying to assign the returned elements from your jQuery selection. You need to iterate over the elements and extract the object form each one. I did not do a isNaN or any error checking here but this works.

$('#button-calc').on('click', function(event) {
  event.preventDefault();
  var total = 0;
  $.each( $('input.input-time'), function(idx, elm) {
    var v = $(elm).val()
    total += parseFloat(v);
  });

  console.log(total);

});

Also I used parseFloat to account for float numbers.

Comments

0

You can use a map/reduce operation here to simplify your code. Instead of looping through your input, you can map() them to return only the value and then reduce() this value array with an addition.

$(function() {
  
  // ADDING NEW INPUT FIELD
  $('.button-plus').on('click', function(event) {
    event.preventDefault();
    
    let inPut = $('.input-time');
    let inPutArr = jQuery.makeArray( inPut );
    // console.log(inPutArr);

    let lastInPut = inPutArr[inPutArr.length-1]; // get last elem of array
    $(lastInPut).addClass('lastone'); // add class to it
    $('.lastone').after('<input type=\"text\" class=\"input-time\">');
    $('.lastone').delay(10).removeClass('lastone');
  });

  // DOING CALCULATION - WORKING NOW
  $('#button-calc').on('click', function(event) {
    event.preventDefault();
    var sum = $('.container-inner input[type=text]').map(function(index, ob) {
    	return parseInt(ob.value);
    }).get().reduce(function(a, b) {
      return a + b;
    });
    console.log(sum);
  });

});
/* IGNORE THE STYLE ;) */

input {
  width: 160px;
  height: 20px;
  padding: 10px 20px 10px 20px;
  border: none;
  border-radius: 20px;
  font-size: 15px;
  display: block;
  margin-bottom: 10px;
}

.container {
  background-color: #dde4fe;
  padding: 20px;
  width: 200px;
  margin-right: 20px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 100px;
}

.button-plus {
  width: 40px;
  height: 40px;
  background-color: #9aadfd;
  color: #fff;
  cursor: pointer;
  margin-top: 10px;
  margin-left: auto;
  margin-right: auto;
  border-radius: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}


#button-calc {
  width: 200px;
  height: 40px;
  background-color: #5677fc;
  color: #fff;
  cursor: pointer;
  margin-top: 20px;
  margin-bottom: 0;
}

.btn-plus {
  width: 20px;
  margin-left: auto;
  margin-right: auto;
  height: 5px;
  background-color: #5677fc;
  border-radius: 5px;
  position: absolute;
}

.plus-1 {
  transition: 0.5s;
  transform: rotate(0deg);
}

.plus-2 {
  transition: 0.5s;
  transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="container">
  <div class="container-inner">
    <input type="text" class="input-time">
    <input type="text" class="input-time">
    <div class="button-plus">
      <div class="btn-plus plus-1"></div>
      <div class="btn-plus plus-2"></div>
    </div>
    <input type="submit" id="button-calc" value="Calculate">
  </div>
</section>

1 Comment

Thanks for another way. :) I've never used reduce before.
0

Your first issue is variable scope, sum doesn't exist at the point you are trying to log it, in my example I define sum outside of any other functions so that it is block level global to your IIFE. The second issue was inside your calculate callback, your inPutV variable was pulling the value of your selector, to iterate and sum them you would need to pull the selector, and then iterate it to get your sum. Third issue, your inputs are type text, not number, so they would need to be converted to numbers before addition would be successful (you could also change your input type, which would be my recommendation).

$(function() {
  let sum = 0
  
  // ADDING NEW INPUT FIELD
  $('.button-plus').on('click', function(event) {
    event.preventDefault();
    
    let inPut = $('.input-time');
    let inPutArr = jQuery.makeArray( inPut );
    // console.log(inPutArr);

    let lastInPut = inPutArr[inPutArr.length-1]; // get last elem of array
    $(lastInPut).addClass('lastone'); // add class to it
    $('.lastone').after('<input type=\"text\" class=\"input-time\">');
    $('.lastone').delay(10).removeClass('lastone');
    
    sum = 0;
  });

  // DOING CALCULATION - NOT WORKING
  $('#button-calc').on('click', function(event) {
    event.preventDefault();
    let inPutV = $('.input-time');
    let inPutVal = jQuery.makeArray( inPutV );
    for (var i = 0; i < inPutVal.length; i++) {
      sum += Number(inPutVal[i].value);
    }
    console.log(sum);
    
    sum = 0;
  });

});
input {
  width: 160px;
  height: 20px;
  padding: 10px 20px 10px 20px;
  border: none;
  border-radius: 20px;
  font-size: 15px;
  display: block;
  margin-bottom: 10px;
}

.container {
  background-color: #dde4fe;
  padding: 20px;
  width: 200px;
  margin-right: 20px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 100px;
}

.button-plus {
  width: 40px;
  height: 40px;
  background-color: #9aadfd;
  color: #fff;
  cursor: pointer;
  margin-top: 10px;
  margin-left: auto;
  margin-right: auto;
  border-radius: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}


#button-calc {
  width: 200px;
  height: 40px;
  background-color: #5677fc;
  color: #fff;
  cursor: pointer;
  margin-top: 20px;
  margin-bottom: 0;
}

.btn-plus {
  width: 20px;
  margin-left: auto;
  margin-right: auto;
  height: 5px;
  background-color: #5677fc;
  border-radius: 5px;
  position: absolute;
}

.plus-1 {
  transition: 0.5s;
  transform: rotate(0deg);
}

.plus-2 {
  transition: 0.5s;
  transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<section class="container">
  <div class="container-inner">
    <input type="text" class="input-time">
    <input type="text" class="input-time">
    <div class="button-plus">
      <div class="btn-plus plus-1"></div>
      <div class="btn-plus plus-2"></div>
    </div>
    <input type="submit" id="button-calc" value="Calculate">
  </div>
</section>

Edit (In Vanilla)

Just for comparison, here is your functionality without any jQuery.

Reference

(function() {
  // Create object to store sum and number of inputs on page
  const data = { sum: 0, inputs: 2, };

  // Event handler for creating new inputs
  const addInput = e => {
    e.preventDefault();
    // create a new input element (eg: $('<input/>');)
    const newInput = document.createElement('input')
    // assign input type, and class to new element
    newInput.type = 'number';
    newInput.classList.add('input-time');

    // get input container (eg: $('.container-inner');)
    const container = document.querySelector('.container-inner')
    // this inserts the new input before the end of the element
    container.insertBefore(newInput, container.children[data.inputs]);

    // update number of inputs, to allow adding more.
    data.inputs += 1;
  };

  // Event handler for getting sum of inputs
  const sumInputs = e => {
    e.preventDefault();
    // get all elements of class input-time (eg: $('.input-time');)
    const inputs = document.querySelectorAll('.input-time');
    data.sum = Object.keys(inputs)                // keys for inputs object in array form
      .filter(key => !isNaN(key))                 // filter out non-numbers
      .map(val => parseInt(inputs[val].value)) .  // map new array from input values
      .reduce((sum, current) => sum + current, 0);// sum array of input values
    console.log(data.sum);
  };

  // get your add button and sum button (eg: $('.button-plus');)
  const buttonAdd = document.querySelector('.button-plus');
  const buttonSum = document.querySelector('#button-calc');

  // create event listeners (eg: $('.button-plus').on('click', addInput);)
  buttonAdd.addEventListener('click', addInput, false);
  buttonSum.addEventListener('click', sumInputs, false);
})();
input {
  width: 160px;
  height: 20px;
  padding: 10px 20px 10px 20px;
  border: none;
  border-radius: 20px;
  font-size: 15px;
  display: block;
  margin-bottom: 10px;
}

.container {
  background-color: #dde4fe;
  padding: 20px;
  width: 200px;
  margin-right: 20px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 100px;
}

.button-plus {
  width: 40px;
  height: 40px;
  background-color: #9aadfd;
  color: #fff;
  cursor: pointer;
  margin-top: 10px;
  margin-left: auto;
  margin-right: auto;
  border-radius: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}


#button-calc {
  width: 200px;
  height: 40px;
  background-color: #5677fc;
  color: #fff;
  cursor: pointer;
  margin-top: 20px;
  margin-bottom: 0;
}

.btn-plus {
  width: 20px;
  margin-left: auto;
  margin-right: auto;
  height: 5px;
  background-color: #5677fc;
  border-radius: 5px;
  position: absolute;
}

.plus-1 {
  transition: 0.5s;
  transform: rotate(0deg);
}

.plus-2 {
  transition: 0.5s;
  transform: rotate(90deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<section class="container">
  <div class="container-inner">
    <input type="number" class="input-time">
    <input type="number" class="input-time">
    <div class="button-plus">
      <div class="btn-plus plus-1"></div>
      <div class="btn-plus plus-2"></div>
    </div>
    <input type="submit" id="button-calc" value="Calculate">
  </div>
</section>

4 Comments

This is very helpful. Thanks. :) If I change the input type to number I would get those arrows to change the value which I don't want. Maybe it's better to change it to number, I'll google it if I can disable them.
So upon further reflection, I think either way you'd have to parse your inputs into numbers here since you aren't using a form element and grabbing form data that way.
Yeah would be the best to do so. Btw thanks for the vanilla code. It looks very complicated.
No trouble, the vanilla is more verbose for sure, jQuery does a lot of the document create element and add event listener stuff behind the scenes. The ES6 arrow functions look fancy, but you could replace them in this context with normal functions with no large code changes. I'll go through and comment it in case you are curious about what's going on :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.