SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    SitePoint Member
    Join Date
    Jun 2008
    Location
    Northern NSW, Australia
    Posts
    6
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question Javascript String to Function issue

    Having an issue trying to assign a .onclick event. I have a string variable and needs to be converted to a function. I know it is not correct in its present state but not sure how it can be done...

    The line in the code is: newNode[i].onclick = eval(ocfunc);

    I am also finding IE bombs at this line, although it could be from the code above: newNode[i].name = "rec" + initrecs + "[]";

    Code:
    function addit(rectype) {
    
    	thisdiv = "Clone" + rectype;
    	anchordiv = "Anchor" + rectype;
    	initrecs = initrecs + 1;
    	var ocfunc = "removeit('recdiv" + initrecs + "','delflag" + initrecs + "');"
    
    	var newNodes = document.getElementById(thisdiv).cloneNode(true)
    	newNodes.id = "recdiv" + initrecs;
    	newNodes.style.display = 'block'
    
        var newNode = newNodes.childNodes;
    	for (var i=0;i<newNode.length;i++) {
          var theName = newNode[i].name;
    	  switch (theName) {
    	  	case "recbtn"	: if ( newNode[i].attachEvent ) {
    							newNode[i].onclick = new eval(ocfunc);
    //							alert( 'IE' );
    						  } else {
    							newNode[i].setAttribute( 'onclick', ocfunc );
    						  }
    						  break;
    		case "flag"		: newNode[i].value = "0"; newNode[i].id = "delflag" + initrecs; break;
    	  }
    	  newNode[i].name = "rec" + initrecs + "[]";
        }
    
    	var insertHere = document.getElementById(anchordiv);
    	insertHere.parentNode.insertBefore(newNodes,insertHere);
    }
    Just outside my actual form code I have;

    Code:
          <div id="Clone1" style="display: none">
          <input name="rec" value="1" type="hidden">
          <input name="rec" value="0" type="hidden">
          <input name="flag" id="" value="0" type="hidden">
          <textarea name="rec" cols=60 rows=3></textarea>
          <select name="rec">
            <option>UserID</option>
            <option value="ROB">ROB</option>
            <option value="JAS">JAS</option>
            <option value="DAN">DAN</option>
            <option value="ALI">ALI</option>
          </select>
          <input value="-" name="recbtn" onclick="removeit('recdiv1','delflag1');" type="button">
    	  </div>
    The PHP code containing the addit function is here (inside form);

    Code:
        echo '          <input type="button" onclick="addit(\'1\');" id="addTask" value="Add Task" />',"\n";
        echo '          <input type="button" onclick="addit(\'2\');" id="addItem" value="Add Hardware" />',"\n";

    Any help in direction would be greatly appreciated!

    Thx
    Jason

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,683
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    Try this:

    Code javascript:
    var ocfunc = function (initrecs) {
        return function () {
            removeit('recdiv' + initrecs, 'delflag' + initrecs);
        }
    }(initrecs);
    ...
    newNode[i].onclick = ocfunc;
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Member
    Join Date
    Jun 2008
    Location
    Northern NSW, Australia
    Posts
    6
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Excellent thanks! Works beautifully. Had to bodgy my code because I was not getting to the "recbtn" case but confirmed your code works nicely! EDIT: fixed the code - was an undefined name causing it to error in IE

    No idea why you have (initrecs) immediately before and after the function part of assigning ocfunc2 however

    Just fyi, code is now;

    Code:
    function addit(rectype) {
    
    	thisdiv = "Clone" + rectype;
    	anchordiv = "Anchor" + rectype;
    	initrecs = initrecs + 1;
    	var ocfunc1 = "removeit('recdiv" + initrecs + "','delflag" + initrecs + "');"
    
        var ocfunc2 = function (initrecs) {
          return function () { removeit('recdiv' + initrecs, 'delflag' + initrecs); }
        }(initrecs);
    
    	var newNodes = document.getElementById(thisdiv).cloneNode(true)
    	newNodes.id = "recdiv" + initrecs;
    	newNodes.style.display = 'block'
    
        var newNode = newNodes.childNodes;
    	for (var i=0;i<newNode.length;i++) {
          var theName = newNode[i].name;
    	  switch (theName) {
    	  	case "recbtn"	: if ( newNode[i].attachEvent ) {
    							newNode[i].onclick = ocfunc2;
    						  } else {
    							newNode[i].setAttribute( 'onclick', ocfunc1 );
    						  }
    						  break;
    		case "flag"		: newNode[i].value = "0"; newNode[i].id = "delflag" + initrecs; break;
    	  }
    	  if (theName) newNode[i].name = "rec" + initrecs + "[]";
        }
    
    	var insertHere = document.getElementById(anchordiv);
    	insertHere.parentNode.insertBefore(newNodes,insertHere);
    }
    (I have assigned different variable names because I need the string version for the setattribute method)

    Again many thanks!

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,683
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by GoorooTech View Post
    No idea why you have (initrecs) immediately before and after the function part of assigning ocfunc2 however
    Currently the following might work

    Code javascript:
    function addit(rectype) {
        initrecs = initrecs + 1;
        var ocfunc = function () {
            removeit('recdiv' + initrecs, 'delflag' + initrecs);
        }
        newNode[i].onclick = ocfunc;
    }

    But it's bad code because initrecs becomes a global variable. If I were to try it out on Firefox then the Firebug addon would yell and scream that initrecs is not defined.

    The other problem is that after you've run addit four times, all of the onclick events are going to run removeit on the fourth div. This is because they don't take the value from when the function was attached to the event, but instead waits until the event is fired before getting the initrecs value.

    So what other ways are there to keep a running count? We can add a property on to the function itself which takes care of the global variable issue. If the initrecs property exists then we'll increment it, and if it doesn't we'll give it a default value.

    Code javascript:
    function addit(rectype) {
        addit.initrecs = addit.initrecs + 1 || 1;
        var ocfunc = function () {
            removeit('recdiv' + addit.initrecs, 'delflag' + addit.initrecs);
        }
        ...
        newNode[i].onclick = ocfunc;
    }

    Now there's no trouble with global variables, but there is still the scoping trouble with the initrecs variable.

    How we get around this is we create a function and pass it the variable that we want to maintain. Here's the commonly understood way to achieve that

    Code javascript:
    var initrecs = 3;
    function myFunc(initrecs) {
        // value of initrecs is 3;
        initrecs = 5;
        // value of initrecs is 5;
    }
    // value of initrecs is 3;
    myFunc(initrecs);
    // value of initrecs is 3;

    This lets us call myFunc and pass initrecs which will be separately maintained while inside of the myFunc function.

    We can also define and call the function at the same time, all in one step.

    Code javascript:
    var initrecs = 3;
    function myFunc (initrecs) {
        // value of initrecs is 3;
        initrecs = 5;
        // value of initrecs is 5;
    }(initrecs);
    // value of initrecs is 3;

    The whole purpose of this is to demonstrate that passing a variable to a function allows the inside variable to become different from the outside variable, without changing the outside one.

    Normally with events you assign a function to them, either by name or by assigning an anonymous function to them. With your code a variable is being created to contain the anonymous function.

    Code javascript:
    var ocfunc2 = function () {
        // initrecs value is variable
        removeit('recdiv' + initrecs, 'delflag' + initrecs);
    };

    In order for initrecs to retain the value that it needs to be, we'll need to pass the variable in to the function in some way. That can be done by calling the function, but with events you can't pass along a value as well. it has to be passed from a containing function, at which point we can return the function that will be able to retain its value.

    Code javascript:
    var ocfunc2 = function () {
        var initrecs = somevalue;
        // initrecs value is now fixed
        return function () {
            removeit('recdiv' + initrecs, 'delflag' + initrecs);
        }
    };

    That is how we come to the working solution. We pass a value to the outer function and use that from the returned one.

    Code javascript:
    var ocfunc2 = function (initrecs) {
        // initrecs now stays what it should be
        return function () {
            removeit('recdiv' + initrecs, 'delflag' + initrecs);
        }
    }(initrecs);

    Now the inner function will use the value that was passed to the outer function, instead of the global variable.

    Here's how we use that with the proposed update to remove the global variable problems.

    Code javascript:
    function addit(rectype) {
        addit.initrecs = addit.initrecs + 1 || 1;
        var ocfunc = function (initrecs) {
            return function () {
                removeit('recdiv' + initrecs, 'delflag' + initrecs);
            }
        }(addit.initrecs);
        ...
        newNode[i].onclick = ocfunc;
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,683
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    I saw something in your code that you might want to tidy up.

    Code javascript:
    case "recbtn":
        if ( newNode[i].attachEvent ) {
            newNode[i].onclick = new eval(ocfunc);
        //  alert( 'IE' );
        } else {
            newNode[i].setAttribute( 'onclick', ocfunc );
        }
        break;

    The onclick method works across all browsers, so with ocfunc actually being a function (instead of a string representation of one) you can use just the following

    Code javascript:
    case "recbtn":
        newNode[i].onclick = ocfunc;
        break;
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    SitePoint Member
    Join Date
    Jun 2008
    Location
    Northern NSW, Australia
    Posts
    6
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Holly... I cannot thank you enough for taking the time out to explain it - it now completely makes sense and I had absolutely no idea!

    And yes, that worked a charm after I clean the code up!

    Again my thanks!! Dynamic form is running really well at the moment!


Tags for this Thread

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
  •