Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery: does not add class to tag or instantly removes it, respectively

First of all, i am new to jQuery. It is no rocket science to create a simple program, but the effect of the following code doesn't make any sense to me. My intention is that after an element with a class ".button" was clicked a new class should be added to that element. Otherwise - clicking somewhere else - the present new class should be removed. Unfortunately, the code in else is executed, even though the condition is true. What are the flaws of my idea? Could you provide a solution to that problem.

$(function() {
  $("*").click(function() {
    var ret = $(this).hasClass("button");

    if (ret) {
      $(".button").addClass("newclass");
    } else {
      $(".button").removeClass("newclass");
    }
  });

  $("form").on("submit", function() {
    return false;
  })
});
.newclass {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<form method="get" action="">
  <input type="text" name="">
  <input type="submit" name="" class="button">
</form>
like image 897
Overflowing Stack Avatar asked Jan 05 '23 10:01

Overflowing Stack


1 Answers

click events bubble from the element that was clicked up through to the document, passing through all elements en route.

So what happens when you click your button is that while the class gets added (because the button has the class button), the click bubbles to the form (which doesn't) and then to body (which doesn't) and then to html (which doesn't). Your handler is called at each level, because you've attached it to all elements, and so the last one (html) wins.

So the minimal change to your code is to stop propagation (tell the event to stop bubbling) when you handle the click:

$("*").click(function(e) {
// Note --------------^

  e.stopPropagation(); // <===

  var ret = $(this).hasClass("button");

  if (ret) {
    $(".button").addClass("newclass");
  } else {
    $(".button").removeClass("newclass");
  }
});

Example:

$(function() {
  $("*").click(function(e) {
    e.stopPropagation();
    
    var ret = $(this).hasClass("button");

    if (ret) {
      $(".button").addClass("newclass");
    } else {
      $(".button").removeClass("newclass");
    }
  });

  $("form").on("submit", function() {
    return false;
  })
});
.newclass {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<form method="get" action="">
  <input type="text" name="">
  <input type="submit" name="" class="button">
</form>

But, I would change your approach entirely. Instead of having a handler on every element in the DOM, I would just attach to document and then inspect e.target:

$(document).click(function(e) {
// ^^^^^^ --- note --------^

  var ret = $(e.target).hasClass("button");
// Note ------^^^^^^^^

  if (ret) {
    $(".button").addClass("newclass");
  } else {
    $(".button").removeClass("newclass");
  }
});

That waits for the click to bubble up to document.body, and then looks at where it bubbled from (e.target).

Example:

$(function() {
  $(document).click(function(e) {
  // Note ------------------------^

    var ret = $(e.target).hasClass("button");

    if (ret) {
      $(".button").addClass("newclass");
    } else {
      $(".button").removeClass("newclass");
    }
  });

  $("form").on("submit", function() {
    return false;
  })
});
.newclass {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<form method="get" action="">
  <input type="text" name="">
  <input type="submit" name="" class="button">
</form>

If you were using an element that could have child elements (like button instead of input), you'd have the situation where the click might originate in a child or descendant of the button. In that case, you might use closest to figure out whether the click passed through a .button element en route to document:

var ret = $(e.target).closest(".button").length;
if (ret) {
    // The element clicked or an ancestor of it has the class...

(There we're relying on the fact that 0 is falsy but all other non-NaN numbers aren't.)

Example:

$(function() {
  $(document).click(function(e) {
  // Note ------------------------^

    var ret = $(e.target).closest(".button").length;

    if (ret) {
      $(".button").addClass("newclass");
    } else {
      $(".button").removeClass("newclass");
    }
  });

  $("form").on("submit", function() {
    return false;
  })
});
.newclass {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<form method="get" action="">
  <input type="text" name="">
  <button class="button">
    Direct <strong>Child</strong>
  </button>
</form>
like image 112
T.J. Crowder Avatar answered Jan 13 '23 08:01

T.J. Crowder