Search code examples
javascripthtmldata-bindingdefineproperty

Object.defineProperty data binding scope


I have been playing with the javascript Object.defineProperty to create data binding. I did get it to work. My question is has to do with something else.

I have a for html query to look for elements with the attribute 'data-bind' and loop through them in a for-loop. From withing the for-loop I call a function to bind them through the setter property:

var cbinder = function(obj, elem, prop) {
      Object.defineProperty(obj, prop, {
        set: function(val) {
          elem.innerHTML = val;
        }
      });
    };

// called from

 var elem = document.querySelectorAll("[data-bind]"); 
 for (var i = 0; i < elem.length; i++) {
            var prop = elem[i].getAttribute("data-bind");
            cbinder(obj, elem[i], prop);
        }

	var obj = {};
	var cbinder = function(obj, elem, prop) {

	  Object.defineProperty(obj, prop, {
	    set: function(val) {
	      elem.innerHTML = val;
	    }
	  });
	};

	window.onload = function() {
	  var elem = document.querySelectorAll("[data-bind]");

	  for (var i = 0; i < elem.length; i++) {
	    var prop = elem[i].getAttribute("data-bind");
	    cbinder(obj, elem[i], prop);
	  }


	  var btn = document.querySelector("#chnbtn");
	  btn.addEventListener("click", function(event) {
	    obj.fname = "John";
	    obj.lname = "Doe";

	  }, false);


	};
<p>
  <span data-bind="fname">my name</span>  <span data-bind="lname">my name</span>
</p>
<p>
  <button id="chnbtn">Change Values</button>
</p>

Since I call this function within the for-loop I figured I might as well integrate it there, but here is where my issue rises. It does not work correctly and only assigns the last value when setting the object. Why is the first example working fine and the second fails, while they are in my opinion similar calls. Can someone clench my curiosity?

   var elem = document.querySelectorAll("[data-bind]");
   for (var i = 0; i < elem.length; i++) {
        var prop = elem[i].getAttribute("data-bind");
        var el = elem[i]
        Object.defineProperty(obj, prop, {
            set: function (val) {
                el.innerHTML = val;
            }
        });
    }

    var obj = {};
    window.onload = function() {
      var elem = document.querySelectorAll("[data-bind]");

      for (var i = 0; i < elem.length; i++) {
        var prop = elem[i].getAttribute("data-bind");
        var el = elem[i]
        Object.defineProperty(obj, prop, {
          set: function(val) {
            el.innerHTML = val;
          }
        });
      }


      var btn = document.querySelector("#chnbtn");
      btn.addEventListener("click", function(event) {
        obj.fname = "John";
        obj.lname = "Doe";

      }, false);


    };
<p>
  <span data-bind="fname">my name</span>  <span data-bind="lname">my name</span>

</p>
<p>
  <button id="chnbtn">Change Values</button>
</p>


Solution

  • The setter function you're assigning to your property has an enduring reference to the el variable, not a copy of it as of when the function was created. So when the function is called, it's trying to use the last value el had in the loop.

    Your original code, putting that off in a function, was the way to go.