SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    352
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    A problem applying 'onclick'' event handlers dynamically

    I run a web site on which subscribing members can advertise holiday accommodation etc. The amount a member pays depends (in part) on the number and type of their properties. In the 'Members Area' (which requires login) a member can see how much their subscription for this year and next year will be. The appropriate year is selected by the member clicking a button. The data is all extracted from a MySQL database.

    Hitherto this has always required a page refresh, but I have been trying to introduce a bit of Ajax, so that only the table gets refreshed. It's all working very well as long as the 'onclick' event handlers are hard coded. I have been trying to assign the onclick event handlers dynamically, but always run into a problem that BOTH the event handlers take on the final value of the variable (k+i) (which should be the year, 2012 or 2013, but always ends up as 2014).

    Code:
    	function prepareHandler() {
    		if (!document.getElementById) return;
    		var inputs = document.getElementsByTagName('input');
    		var len = inputs.length;
    		var k = 2012;
    		for (var i=0; i<len; i++) {
    			var trhd = (k+i);
    			inputs[i].onclick = function() {setRateId(k+i);};
    			alert ("Year = " + (k+i)); // when function runs, shows correct values, 2012, 2013
    		}
    		alert ("final Year = " + (k+i)); // Always 2014 (which I think is to be expected)
    	}
    
    	function setRateId(abcd) {
    		alert ("setRateid called; year = " + abcd); // ALWAYS shows 2014 (unless event handlers are hard-coded)
    		var url = "subsformlogic.php?rateid=" + abcd;
    		return !grabFile(url);
    
    	}
    
    	window.onload = prepareHandler; // disabled when using hard coded event handlers
    It's as though the values set by the line
    Code:
    inputs[i].onclick = function() {setRateId(k+i);};
    aren't being 'frozen', but continue to increment. Surely this should not happen ?

    If I use hard coded event handlers there's no problem at all (the PHP variables evaluate to 2102 and 2013 respectively):-

    HTML Code:
    <p><input type="button" value='This Year' onclick='setRateId(<?php echo $thisyear; ?>)' />
    &nbsp;&nbsp;
    <input type="button"  value='Next Year' onclick='setRateId(<?php echo $nextyear; ?>)' />
    </p>
    In this case there are only two event handlers required, so hard coding them is no hardship. However I'm looking to make similar changes to other pages, where there will be more.

    I think the problem must lie in the function 'prepareHandler' as 'setRateId' runs OK even though it gets duff input (there's an error message if the year exceeds 2013).

    Have I overlooked something very basic ?
    Tim Dawson
    Isle of Mull, Scotland

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ramasaig View Post
    It's as though the values set by the line
    Code:
    inputs[i].onclick = function() {setRateId(k+i);};
    aren't being 'frozen', but continue to increment. Surely this should not happen ?
    You're learning that things are different from what you expected.

    When a function is assigned, it is not evaluated at the time of assignment. Instead, it's when the function is executed that it is evaluated. In your case, it uses the global variable of k and i whenever it is clicked on.

    For things to work properly as you expect them to, you need to pass those variables in to a function. For example:

    Code javascript:
    function assignRateEvent(year) {
        return function () {
            setRateId(year);
        };
    }
    ...
    inputs[i].onclick = assignRateEvent(k + i);

    That way the returned function retains knowledge of the year that you want to use it with.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    352
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thank you, Paul, that works perfectly.

    When a function is assigned, it is not evaluated at the time of assignment. Instead, it's when the function is executed that it is evaluated. In your case, it uses the global variable of k and i whenever it is clicked on.
    Yes, I was beginning to realise that that was my problem, although I didn't express it so elegantly !

    A good day on Ajax, and thanks to your help, a successful one.
    Tim Dawson
    Isle of Mull, Scotland

  4. #4
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    352
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Hello again, Paul,

    I awoke this morning wondering why
    Code:
    	var y = (k+i);
    	buttons[i].onclick = assignRateEvent(y);
    was able to capture and hold the transient value of 'y', but the more 'direct'
    Code:
    	var y = (k+i);
    	buttons[i].onclick = setRateId(y);
    could not. I think it must be because both functions actually run at the time of the 'for' loop ? Clearly running 'setRateId' at that point is not going to get us anywhere. (I've changed the 'input' of my first posting to 'button', but this of course plays no part)

    I set up another function with 'getAttribure' to show the event handlers after they'd been set, but always get a null result (no matter which of the lines above I use). So then I thought I'd try 'setAttribute' as follows:
    Code:
    	var y = (k+i);
    	buttons[i].setAttribute('onclick', 'setRateId(' + y + ')');
    and this does work in Firefox, AND I can 'see' the event handlers using 'getAttribute'. However, I have previously been warned against using 'setAttribute' (in a different context) as it isn't supported everywhere (presumably IE as usual). I'll soon find out.
    Tim Dawson
    Isle of Mull, Scotland

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by ramasaig View Post
    So then I thought I'd try 'setAttribute' as follows:
    Code:
    	var y = (k+i);
    	buttons[i].setAttribute('onclick', 'setRateId(' + y + ')');
    and this does work in Firefox, AND I can 'see' the event handlers using 'getAttribute'.
    The reason why that works is, if y currently equals 2, it's assigning to the onclick event the following:

    Code:
    function () {
        setRateId(2)
    }
    So there is no y value involved in that event anymore.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    352
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Hello Paul, thanks for your reply.

    The reason why that works is, if y currently equals 2, it's assigning to the onclick event the following:...
    Yes, and 'setAttribute' seems a good way to do it for FF. However 'Stack Overflow' is full of dire warnings about using 'setAttribute' in IE:-
    http://stackoverflow.com/questions/9...-to-work-in-ie
    I haven't read the whole article yet, so won't comment further except to say that the author's first suggestion is to use an anonymous function, which is what I was initially trying to do....maybe it's adding the arguments that causes the problem.
    Thanks for your help.
    Tim Dawson
    Isle of Mull, Scotland


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
  •