0

I have a bit of jQuery, but put it here a simplified version to better identify any possible glitch. I provided some comments inline the code to better highlight my attempts and also the problem. Please find my comment inline the codes.

My problem is the "Clear" button, clears out any input previously clicked, while it should only clear the current active input. Smells like event bubbling issue, but I couldn't identify it by now, so your help is very much appreciated.

The workflow is like this:

  1. I click input.trigger (input[name="a"]) inside .container

  2. div#popup is popped out/ launched

  3. I click a button "Clear" inside the #popup, it does clear the input[name="a"]. I do a "revert" to put back the value, good so far, if some default value is there, it reverts (this part is not included in the code, because it is not the main problem, if the following is solved). Close it. So input "a" was cleared, but later reverted with its default value via stored data-default.

  4. Good, so far.

  5. I click another input.trigger (input[name="b"]), #popup launched, more actions.. good, but ....

  6. I click "Clear" button, it clears input "a" and "b", while it is expected to clear only current input.trigger[name="b"] (not with previous inputs, "a").

The solution here doesn't help yet.

$('.container').on('focus', 'input.trigger', function(e) {
   e.preventDefault();
   // First fix attempt, no use
   // https://stackoverflow.com/questions/9153501/how-do-you-stop-children-from-propagating-an-event-triggered-by-a-live-delegate
   // Trying to solve problem with event bubbling, but no use
   if (e.target != this){ 
     return true; 
   }
   var input = $(this);
   // somewhere added a class focused on input focused, please ignore.
   // we add blur here to make any button or input inside popup clickable.
   $('input:not(.focused)').blur();

  // Added dynamic data("popup") based on input name (a, b, c, d)
  $("#popup").data("popup", this.name).css({
    left: "20px",
    top: input.offset().top + 24 + "px"
  })
  // Second fix attempt, no use
  .stop(true, true)
  .show('slow', function () {
    var currentPopup = $(this),
      popupName = currentPopup.data("popup"),
      // Trying to get input/trigger name from data "popup" per clicked input name.
      // I tried putting this after `var input = $(this);` above but put it here later
      // thinking it should solve the problem, but alas not.
      theinput = $('input[name="' + popupName + '"]'),
      // Used for other dynamic interaction with some classes (.a_popup, .b_popup, etc).
      popupid = theinput.data('popupid');

    currentPopup.on('click', '.clear', function(e) {
      // Third fix attempt, no use
      e.stopPropagation();
      // The problem is here:
      // The button clears out any previous (opened) input.trigger,
      // while it should only clears the input with name == current popupName

      // Why this also bubbles to previously opened input.trigger "popupid"
      console.log(popupid); 

      theinput.val("");
    });
    // More buttons here: Revert and Close. But once the "Clear" button issue is solved, this is no issue. So excluded.
  });
 })
.on('blur', 'input.trigger', function (e) {
  e.preventDefault(); // no use
  var hidden = $("#popup"),
    popupName = this.name;
  if (hidden.data("popup") === popupName) {
    hidden.hide().removeData("popup");
  }
});

HTML:

<body>
<div id="page-wrapper">
  <div class="container">
    <form>
      <!-- This input value is dynamically changed via other function -->
      <input class="trigger" name="a" value="one" data-popupid=".a_popup" data-default="a_value">
      <input class="trigger" name="b" value="" data-popupid=".b_popup" data-default="">
      <input class="trigger" name="c" value="three" data-popupid=".c_popup" data-default="c_value">
      <input class="trigger" name="d" value="four" data-popupid=".d_popup" data-default="d_value">
    <form>

    <!-- Ignore below divs, but here to have a general idea with above data-popid 
      This is not directly addressed in the code above -->
    <div class="a_popup">Content</div>
    <div class="b_popup">Content</div>
    <div class="c_popup">Content</div>
    <div class="d_popup">Content</div>
  </div>
</div><!-- page wrapper -->

<!-- This popup has data("popup") namee by above inputs (a, b, c, d) assigned dynamically -->
<div id="popup">
  <!-- Sometext and additional inputs -->
  <button class="revert">Revert</button>
  <button class="clear">Clear</button>
  <button class="close">Close</button>
</div> 
</body>

CSS:

#popup {
  background: #fff;
  display: none;
  height: 200px;
  width: 200px;
  z-index: 99999;
  position: absolute;
}

I hope I can make myself clear, but if not I will update if anything I missed from the simplified code above. Thanks for any hint to spot the actual problem.

UPDATE: $('.container').off('input.trigger'); is attached to Close button.

Community
  • 1
  • 1
swan
  • 2,509
  • 3
  • 24
  • 40
  • If you're using a delegated event handler it's too late to stop propagation because the event has already bubbled up to the container before your handler is called. I don't see any use of `.off()` in your code to remove previously bound handlers - you just keep calling `.on()` again binding more and more handlers every time a popup is shown... – nnnnnn Dec 05 '12 at 04:56
  • Thanks, I updated the above. The .off() is attached to Close button, but it doesn't do any good, I am afraid. – swan Dec 05 '12 at 05:05
  • But that won't clear the handler set with `currentPopup.on('click', '.clear', function(e) {...})` will it? – nnnnnn Dec 05 '12 at 05:10
  • I am afraid not. Will put the buttons events out of the popups, and see. Thanks. The buttons are there because there are 3 popups in the page with same classes buttons, only different actions based on actual active popup. And I need easy access based on context/ active popup. – swan Dec 05 '12 at 05:37

1 Answers1

1

I found the JavaScript to be too complicated and hard to follow. So let us simply it:

$(document).ready(function() {
    $('.container').on('focus', 'input.trigger', function(e) {
        var input = $(this);
        var position = input.position();

        $("#popup")
            .data('target', $(e.target))
            .css('top', position.top + 24)
            .css('left', position.left + 20)
            .show();
     });

    $('.clear').on('click', function(e) {
        e.preventDefault();
        var input = $(e.target).closest('#popup').data('target');
        input.val('');
        $(e.target).closest('#popup').hide();
    });
    $('.revert').on('click', function(e) {
        e.preventDefault();
        $(e.target).closest('#popup').hide();
    });
    $('.close').on('click', function(e) {
        e.preventDefault();
        $(e.target).closest('#popup').hide();
    });
});​

And here is a working example of this simpler approach: http://jsfiddle.net/UZ4QM/

Adding back the bling

Now we can add the bling back:

$(document).ready(function() {
    $('.container')
        .on('focus', 'input.trigger', function(e) {
            var input = $(this);
            var position = input.position();

            $("#popup")
                .data('target', $(e.target))
                .css({'top':  position.top + 24,
                      'left': position.left + 20 })
                .show('slow');
         })
        .on('blur', 'input.trigger', function(e) {
            $("#popup").hide();
        });

    $('.clear').on('click', function(e) {
        e.preventDefault();
        var input = $(e.target).closest('#popup').data('target');
        input.val('');
        $(e.target).closest('#popup').hide();
    });
    $('.revert').on('click', function(e) {
        e.preventDefault();
        $(e.target).closest('#popup').hide();
    });
    $('.close').on('click', function(e) {
        e.preventDefault();
        $(e.target).closest('#popup').hide();
    });
});​

And here is a working example of it blinged up closer to the original: http://jsfiddle.net/C9JM5/

Cymen
  • 14,079
  • 4
  • 52
  • 72