jQuery - Undesired recursive action on open and close events of a modal window

Viewed 628

I am working on a project that has a jQuery (version 2.2.0) modal window with the following code: Fiddle: https://jsfiddle.net/8f9fh9p0/1/

function buildModalWindow(values) {

  jQuery(document).on('click', '#openWindow', function(e) {

    e.preventDefault();

    // mask effect
    $('#mask').fadeIn(150);
    $('#mask').fadeTo("slow", 0.4);

    // frame effect
    $('#frameModal').fadeIn(500);

    // display the mask and the frame
    $('#mask').css("display", "block");
    $('#frameModal').css("display", "block");

    // HTML content in the frame
    $('#frameModal').html('<form id="formContent" name="formContent" method="post"><div class="myClass"><div class="linha fontmodalbold fontsize14"><label><span class="span-15">Some Form Title</span><span>Form Id</span></label></div><div class="linha"><label class="fontModalBold fontsize12">Atendant Type:</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="correctiveAction" value="1"/>Corrective Action</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="preventiveAction" value="2"/>Preventive Action</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="project" value="3"/>Project</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="contract" value="4"/>Contract</label></div><div class="direita"><input id="close" type="button" class="button" value="Close" /><input type="button" class="button" id="saveButton" value="Save" onclick="javascript: saveTheInformation();" /></div></div></form>');

  });

  // if close button is clicked
  jQuery(document).on('click', '#close', function(e) {

    e.preventDefault();

    $('#formContent').empty();
    $('#formContent').remove();

    $('#frameModal').css("display", "none");
    $('#mask').css("display", "none");

    $('#frameModal').empty();

  });

  // if div #mask is clicked
  jQuery(document).on('click', '#mask', function(e) {

    e.preventDefault();

    $('#formContent').empty();
    $('#formContent').remove();

    $('#frameModal').css("display", "none");
    $('#mask').css("display", "none");

    $('#frameModal').empty();

  });
}
#mask {
  position: absolute;
  left: 0;
  top: 0;
  background-color: #000;
  display: none;
  width: 500px;
  height: 300px;
}

#modalContent .window {
  position: absolute;
  left: 0;
  top: 0;
  width: 440px;
  height: 200px;
  display: none;
  z-index: 9999;
  padding: 20px;
}

#modalContent #frameModal {
  width: 385px;
  height: 300px;
  padding: 5px;
  background-color: #ffffff;
  overflow: auto;
  left: 200;
  top: 150;
}

.close {
  display: block;
  text-align: right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div>
  <input type="button" value="Open Modal Window" id="openWindow" onclick="javascript: buildModalWindow(3);">
</div>
<div id="modalContent">
  <div id="frameModal" class="window"></div>
  <div id="mask"></div>
</div>

The strange thing is, if you click to open the modal window and close it many times, you will face some undesired recursive situation.

If I call a get Ajax method to do something in the database, it behaves as a geometric progression. When I want to register a new report, for example: - First button click following by close or mask click: one record; - Second button click following by close or mask click: 3 records; - Third button click following by close or mask click: 6 records; - And so on.

Crazy, isn't it?

Anybody knows what is goin on and what I have to change in the JavaScript, CSS or HTML to solve that problem?

Thank you in advance.

The shorter, clearer and improved code (with the resolution provided) is shown below. https://jsfiddle.net/r5aff042/

3 Answers

It's not that crazy. Every time the buildModalWindow function runs, you add another "click" handler to the openWindow element. Bizarrely, this is also the element on which you run the buildModalWindow function in the first place, which is confusing logic.You never remove the previously added handlers,so if you've run that function 3 times, then it'll have added those handlers repeatedly. When you click the button it both runs all the existing handlers (hence the multiple records, you'll see multiple ajax requests in the browser tools, too) and adds yet another click handler to run next time.

This is a fixed version. I moved the other event handlers out of your function too, because they also will be getting duplicated, although the effects are not as problematic.

function buildModalWindow(values) {

  // mask effect
  $('#mask').fadeIn(150);
  $('#mask').fadeTo("slow", 0.4);

  // frame effect
  $('#frameModal').fadeIn(500);

  // display the mask and the frame
  $('#mask').css("display", "block");
  $('#frameModal').css("display", "block");

  // HTML content in the frame
  $('#frameModal').html('<form id="formContent" name="formContent" method="post"><div class="myClass"><div class="linha fontmodalbold fontsize14"><label><span class="span-15">Some Form Title</span><span>Form Id</span></label></div><div class="linha"><label class="fontModalBold fontsize12">Atendant Type:</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="correctiveAction" value="1"/>Corrective Action</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="preventiveAction" value="2"/>Preventive Action</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="project" value="3"/>Project</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="contract" value="4"/>Contract</label></div><div class="direita"><input id="close" type="button" class="button" value="Close" /><input type="button" class="button" id="saveButton" value="Save" onclick="javascript: saveTheInformation();" /></div></div></form>');

}

jQuery(document.ready(function() {

      // if close button is clicked
      jQuery(document).on('click', '#close', function(e) {

        e.preventDefault();

        $('#formContent').empty();
        $('#formContent').remove();

        $('#frameModal').css("display", "none");
        $('#mask').css("display", "none");

        $('#frameModal').empty();

      });

      // if div #mask is clicked
      jQuery(document).on('click', '#mask', function(e) {

        e.preventDefault();

        $('#formContent').empty();
        $('#formContent').remove();

        $('#frameModal').css("display", "none");
        $('#mask').css("display", "none");

        $('#frameModal').empty();

      });
    });
#mask {
  position: absolute;
  left: 0;
  top: 0;
  background-color: #000;
  display: none;
  width: 500px;
  height: 300px;
}

#modalContent .window {
  position: absolute;
  left: 0;
  top: 0;
  width: 440px;
  height: 200px;
  display: none;
  z-index: 9999;
  padding: 20px;
}

#modalContent #frameModal {
  width: 385px;
  height: 300px;
  padding: 5px;
  background-color: #ffffff;
  overflow: auto;
  left: 200;
  top: 150;
}

.close {
  display: block;
  text-align: right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div>
  <input type="button" value="Open Modal Window" id="openWindow" onclick="javascript: buildModalWindow(3);">
</div>
<div id="modalContent">
  <div id="frameModal" class="window"></div>
  <div id="mask"></div>
</div>

You can try this:

function buildModalWindow(values){
    jQuery(document).off('click', '#openWindow');
    jQuery(document).off('click', '#close');
    jQuery(document).off('click', '#mask');

    // the rest of your code here...

}

Try this: My example

Your button:

 <div>
     <input type="button" value="Open Modal Window" id="openWindow" data-values="3">
  </div>

OnClick event:

$(document).ready(function() {
    $("#openWindow").click(function(e) {
      e.preventDefault();
      // other way to manage the "values"
      // You also can add the values in hidden inputs 
      buildModalWindow($(this).attr("data-values"));
    });
});

Building the modal: Note that I deleted the additional event within this function, it was "duplicating" each time the button was clicked

function buildModalWindow(values){
   // modal content... 
   // mask effect
    $('#mask').fadeIn(150);
    $('#mask').fadeTo("slow",0.4);

// frame effect
    $('#frameModal').fadeIn(500);

// display the mask and the frame
    $('#mask').css("display","block");
    $('#frameModal').css("display","block");

// HTML content in the frame
$('#frameModal').html('<form id="formContent" name="formContent" method="post"><div class="myClass"><div class="linha fontmodalbold fontsize14"><label><span class="span-15">Some Form Title</span><span>Form Id</span></label></div><div class="linha"><label class="fontModalBold fontsize12">Atendant Type:</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="correctiveAction" value="1"/>Corrective Action</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="preventiveAction" value="2"/>Preventive Action</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="project" value="3"/>Project</label><label class="fontModalBold fontsize12"><input type="radio" name="atendantType" id="contract" value="4"/>Contract</label></div><div class="direita"><input id="close" type="button" class="button" value="Close" /><input type="button" class="button" id="saveButton" value="Save" onclick="javascript: saveTheInformation();" /></div></div></form>');
   // Modal Buttons Events
   jQuery(document).on('click', '#close', function (e) {
      hideAll(); // reuse code
   });
   jQuery(document).on('click', '#mask', function(e) {
      hideAll(); // reuse code
   });
 }

HideAll function:

function hideAll(){
    $('#formContent').empty();
    $('#formContent').remove();
    $('#frameModal').hide();
    $('#mask').hide(); // Hide the mask
    $('#frameModal').empty();
   $('#frameModal').hide(); // also hide the frameModal
}
Related