Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 10 of 10
  1. #1
    Senior Coder
    Join Date
    Sep 2005
    Posts
    1,791
    Thanks
    5
    Thanked 36 Times in 35 Posts

    attaching functions to onclick (closures?)

    The bit of js:
    Code:
    table=$('weektable');
    rows=$A(table.getElementsByTagName('tr'));
    rows.each(function(row) {
    	cells=$A(row.getElementsByTagName('td'));
    	val=cells[0].innerHTML;
    	cells.each(function(cell) {
    		if(cell.className=='weekborder') {
    			cell.onclick=function() {alert(val);}
    		}
    	});
    });
    Some sample HTML:
    Code:
    <table id="weektable">
      <tr>
        <td>Useful Information</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
      </tr>
      <tr>
        <td>Useful Information</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
      </tr>
      <tr>
        <td>Useful Information</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
        <td class="weekborder">&nbsp;</td>
      </tr>
    </table>
    I'm trying to attach onclick events to cells in a table.
    All but the first cell on each row has the class 'weekborder', while the first cell contains information that will be used in the onclick function.
    The problem is, that as 'val' is continually updated, it is using the most-recent value, where I want it to use the value that it had when the function is attached.
    Where am I going wrong?

    the $ functions and .each are from prototype, $ is doc.getElementById, $A converts the node-list into an array, and .each performs the supplied function to each element of an array.
    Last edited by GJay; 05-04-2006 at 02:33 PM.

  • #2
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    Code:
    var table=$('weektable');
    var rows=$A(table.getElementsByTagName('tr'));
    rows.each(function(row) {
    	var cells=$A(row.getElementsByTagName('td'));
    	cells.each((function(val) {
    		return function(cell) {
    			if(cell.className=='weekborder') {
    				cell.onclick=function() {alert(val}
    			}
    		}
    	})(cells[0].innerHTML]));
    });

  • #3
    Senior Coder
    Join Date
    Sep 2005
    Posts
    1,791
    Thanks
    5
    Thanked 36 Times in 35 Posts
    OK, that's great, seems to work fine.
    Could you try explaining it to me though?

    I think that I get the idea of constructing a function that gets used with each, but what is the (cells[0].innerHTML)); bit for?

  • #4
    Senior Coder
    Join Date
    Sep 2005
    Posts
    1,791
    Thanks
    5
    Thanked 36 Times in 35 Posts
    Right, I still don't really understand how it works, and I think I'm having the same problem again, I now have this:
    Code:
    table=$('weektable').getElementsByTagName('table')[0];
    rows=$A(table.getElementsByTagName('tr'));
    rows.each(function(row) {
    	cells=$A(row.getElementsByTagName('td'));
    	val=cells[0].className;
    	i=0;
    	cells.each((function(val) {
    		i++;
    		return function(cell) {
           			if(cell.className=='weekborder') {
    				cell.onclick=function() {
    					alert(i);											}
    			}
    		}
    	})(cells[0].innerHTML));
    });
    So basically, I want that when the 3rd cell in a row is clicked on, it will alert accordingly.

    I'll digress slightly and say what I'm actually trying to do:
    I have a calendar display, that is in a big table. This is made by someone else, so I really don't want to touch their code as that will stop me being able to upgrade when they do.
    A better example of their table is as follows:
    Code:
    <table>
    <tr>
      <td>Controls</td>
      <td>Mon, Apr 30</td>
      <td>Tue, May 1</td>
      <td>Wed, May 2</td>
      etc.
    </tr>
    <tr>
      <td rowspan="4">
        9:00 AM
      </td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      etc.
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td>10:00 AM</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
    etc.
    </table>
    So basically, there are days across the top, and times down the side. Only the hours are labelled, but there are cells for every 15mins. My aim is to provide an onclick for every cell that links to an 'addevent' page, with the start-time, in the form YYYYMMDD or however is easiest, as an argument to the URL.

    Am I approaching this in a sensible-looking way, or does my lack of js-knowledge mean I'm missing something obvious?

  • #5
    Kor
    Kor is offline
    Red Devil Mod Kor's Avatar
    Join Date
    Apr 2003
    Location
    Bucharest, ROMANIA
    Posts
    8,478
    Thanks
    58
    Thanked 379 Times in 375 Posts
    the problem is here:
    return function(cell) {
    if(cell.className=='weekborder') {
    cell.onclick=function() {
    alert(i); }
    }
    }

    You see, the index is called after the loop is ended, so that you will alert always the last value of the i. To avoid that you may attach a "faux" parameter, let's name it ind


    return function(cell) {
    if(cell.className=='weekborder') {
    cell.ind=i;
    cell.onclick=function() {
    alert(this.ind); }
    }
    }

    Now this is a closure, as you are dealing with the this self reference.
    KOR
    Offshore programming
    -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

  • #6
    Senior Coder
    Join Date
    Sep 2005
    Posts
    1,791
    Thanks
    5
    Thanked 36 Times in 35 Posts
    Ok, that makes sense. Thanks.

  • #7
    Senior Coder
    Join Date
    Jul 2005
    Location
    New York, NY
    Posts
    1,084
    Thanks
    4
    Thanked 19 Times in 19 Posts
    a closure contains a frame of reference. You don't need to create expando properties, you can just create local variables. THe frame of reference contains the local variables, the closure contains the frame:
    Code:
    return function(cell)
    {
       var index = i;
       if (cell.className=='weekborder')
      {
        cell.onclick=function()
        {
           alert(index);
        }
      }
    }
    Now you don't need to even worry about the funky "this" keyword. Simply using "this" does not make the function a closure. All functions are closures, it just so happens that the majority of time the only frame of reference that matters is the global one, so we don't notice it.

    So the function you assign to the onclick of the cell contains the frame in which it was defined, thus, the variable "index", which exists in the outer function's running context, is stored in the inner functions frame of reference, and is resolved during the inner functions execution step.

  • #8
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    Also, please use the var keyword to initialize your variables - otherwise you are assigning them globally and all the closures in the world won't help you with that (unless a more local closure has a different variable of the same name).

  • #9
    Senior Coder
    Join Date
    Jul 2005
    Location
    New York, NY
    Posts
    1,084
    Thanks
    4
    Thanked 19 Times in 19 Posts
    indeed, again, another "Feature" of JavaScript. It allows sloppy code to work.

    in the following code, "index" is locally scoped, "cell" is a formal parameter to the function and is therefore locally scoped, "i" is not within the function's local scope so to resolve we go back through the scope chain until "i" is found. In this case, your function is executing inside a for loop with the variable "i" defined, and thus the value of "i" is determined by the loop.

    Fun!

  • #10
    Senior Coder
    Join Date
    Sep 2005
    Posts
    1,791
    Thanks
    5
    Thanked 36 Times in 35 Posts
    I'm still not quite sure I understand, but I've nearly got it working so I'm fairly happy.

    You can probably tell that js isn't one of my strengths, but always looking to improve, so the comments are much appreciated, thanks to all.


  •  

    Posting Permissions

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