Pass object in JavaScript function

I have a situation where I am dynamically building a list of links on a page. When the user clicks on a link I want to call a javascript function passing an object which was created when the page was dynamically built. It appears I am passing the object correctly since when I mouse over the Test link, the browser says [object Object]. The problem is, when the user clicks on the Test link and the testPassObj function is called, a javascript error is thrown saying obj.getFoo is not a function. This indicates to me that obj is not an objectX instance. Any feedback on how I could get this to work would be appreciated.


<html>
<head>
    <title>Test</title>
    <script type="text/javascript">
	
	function objectX() {
		var foo = "";
		
		this.setFoo = function( newFoo ) {
			foo = newFoo;
		};
   	
		this.getFoo = function() {
			return foo;
		};
	}
	
	function testObj() {
		var objInstance = new objectX();
		objInstance.setFoo( "bar" );
		//testPassObj( objInstance ); //-- This works
		
		document.getElementById( "content" ).innerHTML = "<a href='javascript:testPassObj(" + "\\"" + objInstance + "\\"" + ");'>Test</a>";
	}
	
	function testPassObj( obj ) {
		alert( "testing 2: " + obj.getFoo() );  //-- this should say 'bar'
	}

    </script>
</head>
<body onLoad="testObj();">
	<div id="content"></div>
</body>
</html>

The problem is because you’re interpolating the objInstance into a string. When that happens, you lose the reference to the object itself and you get instead the string “[object Object]”.

You’ll have to use less strings and more object references. Something like…

// get content div
var content = document.getElementById('content');

// render content markup
content.innerHTML = '<a href="#">Test</a>';

// bind content events
content.getElementsByTagName('a')[0].onclick = function () {
    testPassObj(objInstance);

    return false;
};

Thank you for the feedback Jeff. My example is scaled down. What I have is a div data area where the data is built dynamically with JavaScript. Each line of data has a link and when the user clicks the link, I would like to pass an object to a JavaScript function instead of passing a long list a parameters. Any ideas on how I could accomplish this? I know the example I provided was very scaled down, I was just trying to pride an example of the design concern I am trying to improve.

The solution would still be mostly the same as I showed. The only difference would be that you would select all A tags rather than just the first, and you would iterate over them to assign the onclick event.

Thank you again for the feedback. My first response probably showed that I was not really grasping your post. I took some time and researched a few concepts and it makes more sense now. I have made some updates aligning the example closely that how my code is but have come across a new problem. In the updated code, I can now pass an object but it is always passing the same object. At a high level I think I understand what is going on. JavaScript is passing the object obj as a reference instead of passing obj by value. Is there a way to pass the obj by value or is there a way to dynamically create a variable name? I did Google the dynamic variable names and it does not sound possible but I thought I would still ask. When I run this code, both links say Test 2. Any help is appreciated.


<html>
<head>
    <title>Test</title>
    <script type="text/javascript">
	
	function objectX() {
		var foo = "";
		
		this.setFoo = function( newFoo ) {
			foo = newFoo;
		};
   	
		this.getFoo = function() {
			return foo;
		};
	}

	function testObj() {
		var content = document.getElementById('content');
		content.innerHTML = '<a href="#" id="Test1">Test 1</a><a href="#" id="Test2">Test 2</a>';
		
		for( var i = 1; i < 3; i++ ) {
			var obj = new objectX();
			obj.setFoo( "Test " + i );

			document.getElementById("Test" + i ).onclick = function () {
				testPassObj(obj);
			};			
		}
	}
	
	function testPassObj( obj ) {
		alert( "object: " + obj.getFoo() );
	}

    </script>
</head>
<body onLoad="testObj();">
	<div id="content"></div>
</body>
</html>

Unfortunately I’ll have to push you into the deep end with closures.

The issue is that the onclick function doesn’t execute right away. It’s executed at some unknown future time, when and if the element is clicked. But in the mean time, the for loop continues, and the variable obj is assigned a new value. What we need to do is save the value of obj at each iteration so that we’re creating new variables rather than overwriting the same one. We can do that with a self-executing function. In JavaScript, a function creates a new scope, and we can use that new scope to keep a private copy of obj at each iteration.

        for( var i = 1; i < 3; i++ ) {
            var obj = new objectX();
            obj.setFoo( "Test " + i );

            // pass obj as an argument and receive it as a parameter
            // in order to keep a private reference to its current value
            (function (obj) {
                document.getElementById("Test" + i ).onclick = function () {
                    testPassObj(obj);
                };
            }(obj));
        }

A helpful way to understand why this works, is to move the function out to a separate named function.


function createTestClickHandler(obj) {
    return function () {
        testPassObj(obj);
    };
}

var  obj, i;

for (i = 1; i < 3; i++) {
    obj = new objectX();
    obj.setFoo( "Test " + i );

    // pass obj as an argument and receive it as a parameter
    // in order to keep a private reference to its current value
    document.getElementById("Test" + i).onclick = createTestClickHandler(obj);
}
}

Now it becomes clearer that the handler function is returning another function, and that execution of the returned function is left waiting until the onclick event occurs.
Functions retain access to the variables from their parent function, so the returned function retains knowledge of the obj variable from the handler function.

It works!!! I appreciate the feedback from both of you.

You have also introduced me to what seems to be a very powerful concept, closures. I like developing object oriented code whenever possible but have obviously had challenges with JavaScript. I did read the link you provided and it appears that closures allow you to implement more object oriented code in JavaScript. What is confusing to me is that if JavaScript does have the ability to retain objects even between user clicks, why is it not implemented in all of its code? Why do I first have to add the <a href> link to the page before I can assign an onclick handler that has an object as a parameter? Maybe I approached my DHTML code incorrectly in that I built the data in chunks of 20 and then added it to the page. Is it a best practice to append data to the page using innerHTML right as you are building code? Sorry for some many questions… its just making me think if I approached my page design correctly from the beginning. I have developed using JavaScript for years but have not really dealt with some of these issues until now.

You don’t have to add the link to the page first. You can create the link as an object, and then do what you like with that link before adding it to the page.

Here is your current testObj function:


function testObj() {
    var content = document.getElementById('content');
    content.innerHTML = '<a href="#" id="Test1">Test 1</a><a href="#" id="Test2">Test 2</a>';
    
    for( var i = 1; i < 3; i++ ) {
        var obj = new objectX();
        obj.setFoo( "Test " + i );

        document.getElementById("Test" + i ).onclick = function () { 
            testPassObj(obj);
        };			
    }
}

Instead of using innerHTML, you can create a document fragment (which is an empty container) and add elements to that fragment before adding that fragment to the page.


function testObj() {
    var content = document.getElementById('content'),
        links = document.createDocumentFragment(),
        i;

    for (i = 0; i < 2; i++) {
        links.appendChild(createLink(i + 1));
    }

    content.innerHTML = ''; // clear contents
    content.appendChild(links);
}

Since the body of the loop has now grown to be several lines, I’ve moved that loop content out to a separate function to help keep things nice and simple.
Also, using a loop that goes from 0 to 2 helps to reinforce to us that only two items are being created.


function createLink(num) {
    var link = document.createElement('a'),
        obj = new objectX();

    obj.setFoo('Test ' + num);

    link.id = 'Test' + num;
    link.href = '#';
    link.onclick = function () {
        testPassObj(obj);
    };
}

Notice also too that because the new object is being created from the createLink function, that the function assigned to the onclick event is now able to easily retain a reference to that object due to how a function retains knowledge of the variables of the function from where it was created.

Each time that you touch the DOM (document object model) of the page, you incur a performance penalty due to the page update resulting in the CSS reflowing itself. So the less times that you update the DOM page, the better.

That’s also a factor in to why it’s preferable to create each link in a document fragment that’s separate from the page, before adding them all together to the page.

@TryingToLearn

With you on this trying to learn bit. Every time I come back to do some JavaScript I find they have moved the goal posts somewhat. I must add though it has been for the better.

This time around I have been using jslint extensively to help get it right. FBH you need a lot of patience and time to adhere to the standard they set but worth it in the end.

JSLint complains about Jeff Mott’s version with don’t make functions in loop whereas paul_wilkins passes.

Have many questions of my own that needs further research but will not hijack your thread.

EDIT: was referring to paul_wilkins response #7
now to go and peruse his new response

I believe that the main reason for not making functions within a loop is to help remove any confusion about variables that are used, and the scope that they are in.
There can also be some caching and performance benefits from using a consistent function scope to return the function that will be used too.

Paul_Wilkins, your JavaScript knowledge is very impressive. I very much appreciate your detailed feedback. Jeff Mott, I also appreciate your contributions. The feedback provided here makes sense and I will have to keep these concepts in mind as I continue JavaScript development.