Search code examples
javascripthtmlcssscoping

JavaScript Scoping and Closure Syntax


I am having problems with scoping and closure in a JavaScript snippet. I am trying to update both the class and onclick properties of a button. Basically this should toggle between "High" and "Low". I could get the function to execute without trying to rescope the parameters but it was not successfully updating the properties. So, I tried to rescope it to the code below and I am now getting an error that channel is not defined. Can anyone help me out with the syntax here? I need the inner function to be able to evaluate both channel and value.

I know the code as provided will not work because of the asynchronous callback. I just can't seem to get the scoping syntax correct for the onreadystatechange function. Please let me know if I can provide any more clarification for the problem I am having. Thanks.

function setDio(channel, value) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = (function(channel,value) { return function(){
        if (this.readyState === 4)
		{
			if (this.status === 200) {
				document.getElementById('ok-msg').innerHTML = 'Digital Output successfully set to '+value+'.';
				document.getElementById('ok-alert').style.display = 'block';
				if(value===1)
				{
					document.getElementById('dio'+channel+'-high').class = 'ibradioactive';
					document.getElementById('dio'+channel+'-low').class = 'ibradioinactive';
					document.getElementById('dio'+channel+'-low').onclick = function(){ setDio(channel,0);};;
					document.getElementById('dio'+channel+'-high').onclick = function(){} ;;
				}
				else
				{
					document.getElementById('dio'+channel+'-high').class = 'ibradioinactive';
					document.getElementById('dio'+channel+'-low').class = 'ibradioactive';
					document.getElementById('dio'+channel+'-high').onclick = function(){ setDio(channel,1);};;
					document.getElementById('dio'+channel+'-low').onclick = function(){} ;;
				}
			}
			else
			{
				document.getElementById('error-msg').innerHTML = 'Digital Output set failed ('+ this.status +'): '+this.responseText;
				document.getElementById('error-alert').style.display = 'block';
			}
        }
    };})(channel,value);
    xmlhttp.open('POST', '/dio/' + channel, true);
    xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xmlhttp.send('dio-value=' + value);
}
.alert,.ok-alert,.error-alert,.info-alert,.warning-alert {
font-family:arial;
font-weight:700;
color:#FFF;
margin-bottom:15px;
border-radius:20px;
padding:20px;
display:none;
}

.ok-alert {
background-color:#4CAF50;
}

.error-alert {
background-color:#f44336;
}

.info-alert {
background-color:#2196F3;
}

.warning-alert {
background-color:#ff9800;
}

.alert-close {
margin-left:15px;
color:#FFF;
font-weight:700;
float:right;
font-size:22px;
line-height:20px;
cursor:pointer;
transition:.3s;
}

.inner-group {
display:inline-block;
border:0 solid #000;
font-family:arial;
font-weight:700;
font-size:12pt;
background:#ccc;
border-radius:15px;
vertical-align:text-top;
box-shadow:4px 4px 10px #66a;
margin:10px;
padding:10px 4px;
transition:1s;
}
.ibradioactive {
background:#11AF11;
}

.ibradioactive span {
color:#fff;
}

.ibradioinactive span {
color:#000;
}

.ibradioinactive {
background:#ddd;
}

.ibradioinactive:hover {
background-color:#acf;
}

.ibradio,.ibradioactive,.ibradioinactive {
width:50px;
height:50px;
cursor:pointer;
box-shadow:3px 3px 2px #000;
-webkit-box-shadow:3px 3px 2px #000;
-moz-box-shadow:3px 3px 2px #000;
font-weight:700;
color:#000;
border-radius:40px;
border:1px solid #999;
margin:10px 10px 10px 20px;
padding:5px;
}
label {
display:inline-block;
width:125px;
padding:3px 10px;
}
<div id="error-alert" class="error-alert">
  <span class="alert-close" onclick="this.parentElement.style.display='none';">&times;</span> 
  <span id="error-msg">This is an error alert box.</span>
</div>
<div id="ok-alert" class="ok-alert">
  <span class="alert-close" onclick="this.parentElement.style.display='none';">&times;</span> 
  <span id="ok-msg">This is a success alert box.</span>
</div>
<div id="info-alert" class="info-alert">
  <span class="alert-close" onclick="this.parentElement.style.display='none';">&times;</span> 
  <span id="info-msg">This is an info alert box.</span>
</div>
<div id="warning-alert" class="warning-alert">
  <span class="alert-close" onclick="this.parentElement.style.display='none';">&times;</span> 
  <span id="warning-msg">This is an warning alert box.</span>
</div>
<br />

<div class="inner-group" style="width:550px;">
<label class="l2">Digital I/O #1</label><br>
<label class="l2" style="width:175px">Mode: OUTPUT</label><br />
<label class="l2">New State:</label>
<button class ="ibradioactive" type="submit" id="dio1-high" name="dio1-high" onclick="setDio(1,1)"><span>HIGH</span></button>
<button class ="ibradioinactive" type="submit" id="dio1-low" name="dio1-low" onclick="setDio(1,0)"><span>LOW</span></button>
</div>

EDIT:

Here is a simplified version of my original code that wasn't working:

function setDio(channel, value) {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
    if (this.readyState === 4)
    {
      if (this.status === 200) {
        if(value === 1)
        {
          document.getElementById('dio'+channel+'-high').class = 'ibradioactive';
          document.getElementById('dio'+channel+'-low').class = 'ibradioinactive';
        }
        else
        {
          document.getElementById('dio'+channel+'-high').class = 'ibradioinactive';
          document.getElementById('dio'+channel+'-low').class = 'ibradioactive';
        }
      }
    }
  };
  xmlhttp.open('POST', '/dio/' + channel, true);
  xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  xmlhttp.send('dio-value=' + value);
}

Because it wasn't working, I tried re-scoping and adding closure to make sure that the function was evaluating the parameter values that were used at call time versus picking up default values.

The classes were not changing as expected and, in fact, the asynchronous request was not actually being picked up by the server. So, can anyone see why this particular function would not work properly? I have similar functions that are just updating simple innerHTML values successfully. Changing the class, however, does not seem to be working.

EDIT 2:

So, I discovered part of my problem. The reason the class was not changing is that I was trying to set the class with text instead of an object. Changing to className seems to have cleared that up. I also went back to not using any extra closure scoping and it may be working properly now. Still doing testing to verify all the changes.


Solution

  • I am not sure why there is need for explicit closure defined..

    xmlhttp.onreadystatechange = function() { 
      console.log(channel); // parent function parameter can still be accessed here.
    }