J Hunt J Hunt - 4 months ago 19
Javascript Question

Avoid repeating jQuery code

If I have code like what is shown below, is there any way of avoiding the need of repeating the code over and over again?

$("#ClearSign1").click(function () {
signaturePad1.clear();
});
$("#ClearSign2").click(function () {
signaturePad2.clear();
});
$("#ClearSign3").click(function () {
signaturePad3.clear();
});
$("#ClearSign4").click(function () {
signaturePad4.clear();
});
$("#ClearSign5").click(function () {
signaturePad5.clear();
});


I can do this:

for(var i = 1; i < 6; i++) {
$("#ClearSign" + i).click(function () {
signaturePad1.clear();
});
}


But how do I handle the signaturePad1 variable? I need it to have the 1 updated to 2, 3, 4, 5 as looping through.

Answer

Well, the most obvious way to clean this up without altering much of your code is to invoke the signaturePad# method on the object on which its scoped to, using the bracket notation. In the outer most scope, that is the window object:

ES5 solution: You must wrap the invocation in an IIFE, which will maintain the value of i properly for each callback within the closure of that IIFE.

for(var i = 1; i < 6; i++) {
  $("#ClearSign" + i).click(function () {
   (function(idx) {
     // in here idx is always the proper i value
     window['signaturePad' + idx].clear();
   })(i); 
  });
}

Or you can create an array of your clearSign functions and use a forEach, which would maintain the proper scope. Or create an array of click callbacks, as Pranav C Balan showed.

ES6 solution: (use let instead of var):

for(let i = 1; i < 6; i++) {
  $("#ClearSign" + i).click(function () {
     window['signaturePad' + i].clear();
  });
}

Using let makes i block scoped within the loop block. This properly captures the value for the callbacks and keeps us from polluting the global scope. Not to mention, it keeps the code more readable and concise - nested IIFE's inside for loops don't really add to code clarity and can cause confusions.

Comments