SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    SitePoint Guru
    Join Date
    Nov 2005
    Location
    Midwest
    Posts
    777
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    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.

    Code:
    <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>

  2. #2
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,278
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    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...

    PHP Code:
    // 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;
    }; 
    "First make it work. Then make it better."

  3. #3
    SitePoint Guru
    Join Date
    Nov 2005
    Location
    Midwest
    Posts
    777
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  4. #4
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,278
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    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.
    "First make it work. Then make it better."

  5. #5
    SitePoint Guru
    Join Date
    Nov 2005
    Location
    Midwest
    Posts
    777
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

    Code:
    <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>

  6. #6
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,278
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    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.

    Code JavaScript:
            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));
            }
    "First make it work. Then make it better."

  7. #7
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,716
    Mentioned
    103 Post(s)
    Tagged
    4 Thread(s)
    A helpful way to understand why this works, is to move the function out to a separate named function.

    Code javascript:
    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.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  8. #8
    SitePoint Guru
    Join Date
    Nov 2005
    Location
    Midwest
    Posts
    777
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  9. #9
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,716
    Mentioned
    103 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by TryingToLearn View Post
    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?
    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:

    Code:
    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.

    Code javascript:
    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.

    Code javascript:
    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.

    Quote Originally Posted by TryingToLearn View Post
    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?
    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.
    Last edited by paul_wilkins; Dec 2, 2012 at 07:01. Reason: move createLink out to a separate example of code
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  10. #10
    SitePoint Member
    Join Date
    Jun 2012
    Location
    au2650
    Posts
    10
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @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

  11. #11
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,716
    Mentioned
    103 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by olePhart View Post
    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.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  12. #12
    SitePoint Guru
    Join Date
    Nov 2005
    Location
    Midwest
    Posts
    777
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •