5

I've a page with two input fields. I've a JS object (info) containing 'reference' and a 'value' field of each item. For each item, there's a corresponding 'input' field with matched by 'class' attribute. When a user updates a matching input field, I want to add it's 'value' in the info object.

The problem I'm experiencing is that it's putting the value in the last item in the array (location.value) for either input.

Can anyone help me with where I'm going wrong please? (I could see solutions using 'each' where data for all inputs needs to be added to an array/object. I'm stuck on getting the data for matched fields.)

$(document).ready(function() {
  var info = {
    name: {
      ref: "a2350",
      value: ""
    },
    location: {
      ref: "a2351",
      value: ""
    }
  };
  for (var p in info) {
    if (info.hasOwnProperty(p)) {
      $('input.' + p).focusout(function() {
        var val = $(this).val();
        info[p].value = val; // Only setting value on the last item in array!
        //console.log(p);   /// p = always location!
        out();
      })
    }
  }

  function out() {
    console.log(info);
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
Emily Rose
  • 95
  • 8
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Serge K. Aug 23 '17 at 14:34
  • More simply, the variable `p` in the line `info[p].value = val;` will always be the last property of your object. – Namaskar Aug 23 '17 at 14:37
  • I wonder if it would make more sense to $('input') and each over them, checking if this.name is in the object and if so info[this.name].value = this.value – Taplar Aug 23 '17 at 14:40

3 Answers3

5

Your issue is because p will be location when the loop ends. Therefore all the click handlers will update the location object, regardless of which element they are attached to.

To fix this you can use a closure to retain the scope of the p at the point the event handler logic was created. Also note that hasOwnProperty() is moot when you're looping through the properties of the object; it has to exist for the iteration to happen. Try this:

$(document).ready(function() {
  var info = {
    name: {
      ref: "a2350",
      value: ""
    },
    location: {
      ref: "a2351",
      value: ""
    }
  };

  for (var p in info) {
    (function(p) {
      $('input.' + p).focusout(function() {
        var val = $(this).val();
        info[p].value = val;
        out();
      })
    })(p);
  }

  function out() {
    console.log(info);
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<input type="text" class="name" />
<input type="text" class="location" value="" />

Alternatively, you can avoid the loop and associated closure by using the class on the input to retrieve the required object:

$(document).ready(function() {
  var info = {
    name: {
      ref: "a2350",
      value: ""
    },
    location: {
      ref: "a2351",
      value: ""
    }
  };

  $('input').focusout(function() {
    var className = $(this).attr('class');
    if (info.hasOwnProperty(className))
      info[className].value = this.value;
    out();
  });

  function out() {
    console.log(info);
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<input type="text" class="name" />
<input type="text" class="location" value="" />
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
2

You can simply use the class attribute to update the object by key:

$(document).ready(function(){
  var info = {
    name: {
      ref: "a2350",
      value: ""
    },
    location: {
      ref: "a2351",
      value: ""
    }
  };
  
  $('input').focusout(function() {
      var className = $(this).attr('class');
      var inputValue = $(this).val();
      if (info.hasOwnProperty(className)) {
        info[className].value = inputValue;
      } else {
        // If you want to add unknown inputs to the object 
        info[className] = {ref: "", value: inputValue};
      }
          
      console.log(info);
    });
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<input type="text" class="name" />
<input type="text" class="location" value="" />
<input type="text" class="not_exists" value="" />
aabilio
  • 1,697
  • 12
  • 19
0

You need something more like:

<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>

<script>

$(document).ready(function(){

    var info = {
      name: {
        ref: "a2350",
        value: ""
      },
      location: {
        ref: "a2351",
        value: ""
      }
    };

    for (var p in info) 
    {
        if ( info.hasOwnProperty(p) ) 
        {
            doSomethingWith(p);
        } 
    }

    function doSomethingWith(p)
    {
        $('input.' + p).focusout(function()
        {
            var val = $(this).val();
            info[p].value = val;

            console.log(p);   /// p = the class of the input now.
        });
    }

});

</script>


<input type="text" class="name" />
<input type="text" class="location" value="" />
Stuart
  • 6,630
  • 2
  • 24
  • 40