switch on/off javascript - javascript

I am trying to make a swith on / off javascript, but I have a problem: I click always comes in my first class despite my class change.
I have always the same result : on->on or off->off. I check in Firebug my html is changed correctly...
Here my simplified code :
$('.off').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
alert('off');
$(this).removeClass('off');
$(this).addClass('on');
});
$('.on').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
alert('on');
$(this).removeClass('on');
$(this).addClass('off');
});
if anyone has a suggestion, I would be very grateful !!

The event handlers are bound on pageload, so changing the class won't change the event handlers, as they are attach to whatever elements existed at the time of binding. To attach them to future elements, i.e. when you change the classes, you'll need delegated event handlers, but an easier solution in my opinion is to just toggle the classes or use a flag:
$('.off').on('click', function(e) {
e.preventDefault();
var state = $(this).is('.off') ? 'on' : 'off';
alert(state);
$(this).toggleClass('off on');
});
FIDDLE
It can be confusing, but even if you remove the .off class, the event handler is still bound to the same element, as it had the .off class at the time of binding!

Either .off or .on does not exist when you first bind with .on. You need to use event delegation, or you can rewrite this so that it's bound to another class and maintain on/off state. I will show you both methods.
Delegation
$(document).on('click', '.off', function(e) {
$(this).removeClass('off').addClass('on');
}).on('click', '.on', function(e) {
$(this).removeClass('on').addClass('off');
});
http://jsfiddle.net/ExplosionPIlls/2PHJg/
Internal state
$(".switch").on('click', function () {
if ($(this).is('.off')) {
$(this).removeClass('off').addClass('on');
}
else {
$(this).removeClass('on').addClass('off');
}
});
http://jsfiddle.net/ExplosionPIlls/2PHJg/1/

This problem is caused because $('.on') and $('.off') only gets evaluated once, when your javascript code runs.
You have to use event delegation to solve the problem. Modify the code the following way. I describe below what it means.
$('#container').on('click', '.off', function(e) {
e.stopPropagation();
e.preventDefault();
alert('off');
$(this).removeClass('off');
$(this).addClass('on');
});
$('#container').on('click', '.on', function(e) {
e.stopPropagation();
e.preventDefault();
alert('on');
$(this).removeClass('on');
$(this).addClass('off');
});
In the place of #container use a selector which selects a parent of your .on / .off elements. In the second parameter of your on() call, youy have to specify the element for which you want to listen for events.
This way the event handler will run properly, even if the class of your elements change.

The problem is that the event handlers are bound when the code first runs, so when the class changes, the event handlers aren't rebound. One way to fix this is to assign the element a class that doesn't change, and use that for the event handler.
Example:
$('.button').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).toggleClass('off on');
});
You can see a working example at http://codepen.io/skimberk1/pen/GJhna.

Related

jQuery not rebinding event to handler after unbinding

I have the following jQuery code:
$(document).on('focusout', '#element', function(e){
alert('test');
});
$(document).on('click', '#element_b', function(e){
$(document).off('focusout','#element');
/* do other stuff */
$(document).on('focusout','#element',function(){});
});
On clicking on #element_b, the event bound to #element is supposed to get off and then back on but for some reason after it goes off, it doesn't return back on. How to get it back on?
In order to be able to re-bind a handler, the handler function must be available for reference at the point it would be re-bound. To achieve that, just define the handler function with a name:
function focusOutHandler(e) {
alert("test");
}
$(document).on("focusout", "#element", focusOutHandler);
Then you can also reference the function in your "click" handler:
$(document).on('click', '#element_b', function(e){
$(document).off('focusout','#element');
/* do other stuff */
$(document).on('focusout', '#element', focusOutHandler);
});
Now, that said, that operation really doesn't make a lot of sense in the context of your question. While the "click" handler code is running, the "focusout" code will not run anyway. Thus, removing the handler at the start of the code and then adding it back at the end won't have any noticeable effect.
I would personally argue for a different approach so you can stop modifying the bindings. Instead change your matcher so it is more restrictive.
$(document).on('focusout', '#element:not(.restricted)', function(e){
alert('test');
});
If you made the selector like this, all you would have to do to "toggle" it on or off is add/remove the restricted class to the element, or whatever more restrictive matcher you give the delegate.

Removing a class doesn't disable the event listener function associated with the removed class

I did a fiddle to demonstrate my problem.
I am having problem to turn off a function activated by a class, any ideas?
$(document).ready(function(){
$('.changeText').click(function(){
$(this).html( $(this).html() == 'Test' ? 'Changed' : 'Test' );
});
$('.changeBG').click(function(){
$(this).css('background-color', 'red');
});
/* in some cases I need to turn off changeBG function */
$('.changeBG').removeClass('changeBG');
// but if you click the div it still turns into red.
});
Thanks in advance.
You could delegate the event handler to a common ancestor.
In doing so, it will only work if that element has that specific class because a check is made when the click event is actually fired (rather than when the event handler is attached).
Example Here
$(document).on('click', '.changeBG', function(){
$(this).css('background-color', 'red');
});
In the example above, document is the common ancestor. Depending on your markup, you will probably want to change that to the closest, constant, ancestor element so that the event isn't fired every time you click on the document.
Alternatively, you could also use the .off() method, and remove that specific event handler by utilizing event namespaces.
You can attach a specific click event named click.changeBG:
$('.changeBG').on('click.changeBG', function(){
$(this).css('background-color', 'red');
});
Then remove that specific event using .off('click.changeBG'):
Example Here
$('.changeBG').removeClass('changeBG').off('click.changeBG');
You need to remove the handler directly:
$('.changeBG').off('click')
Note: off is jQuery 1.7+, otherwise use unbind.
What happens is that the line $('.changeText').click(... attaches an handler to all elements with class changeText. Removing the class does not remove the handler.
This is because the handler is attached to the element and not the class. The class you referred while attaching is just a filter. You have just changed the class. Not the event that is associated with it:
// Do this
$('.changeBG').removeClass('.changeBG').off("click");
Or, when you click check for the class:
$('.changeBG').click(function(){
if ($(this).hasClass("changeBG"))
$(this).css('background-color', 'red');
});

Trigger click on child but prevent event propagation to parent

How do I trigger click on a child element without letting the click event bubble upto the parent & thereby create a infinite trigger loop of click event.
For example I have like this:
<li>
<a> ... </a>
</li>
I want to trigger a click event on a whenever li is clicked, but to prevent infinite trigger loop, I should avoid event propagation upto parent. How do I do that ?
Something like this?
$('li').on('click','a', function(e) {
e.stopPropagation();
// your code here...
});
By using .stopPropagation() on the anchor you can stop the bubbling. You should be able to use something like:
$('a').click(function (e) {
e.stopPropagation();
console.log('link');
});
$('li').click(function () {
$('a').click();
});
jsFiddle example
You do not state exactly the behavior you want from the click on the link, but if you simply want to stop the click being handled at all, use preventDefault() on the event argument of click:
$('li').on('click','a', function(e) {
e.preventDefault();
});
If you just want to stop the parent seeing the event use stopPropagation() instead:
$('li').on('click','a', function(e) {
e.stopPropagation();
});
The other option is return false, which does the same as both preventDefault() and stopPropagation()
$('li').on('click','a', function(e) {
return false;
});
use event.stopPropagation()() in the event callback.
$('selector').click(function(event){
event.stopPropagation()();
})
Parse the tag name on the parent click handler. (You could also look at class name or ID, among other event filters.) If desirable child tag is detected, then stop the propagation and return true so that this event runs. If desirable child tag is not detected, then prevent default event and return false. Thus, clicking on LI will trigger the click on A without a runaway event loop, and clicking on A will proceed properly without a runaway event loop either. Note also that you can't just fire the .click() event on the tag. You need to send it deeper into the native DOM element, using [0].click(), as I have shown below.
$('LI').click(function(e){
var sTag = e.target.tagName.toUpperCase();
if (sTag == 'A') {
console.log('DEBUG: A click');
e.stopImmediatePropagation();
return true;
} else {
console.log('DEBUG: ' + sTag + ' click');
e.preventDefault();
// the following won't work without the [0]
$(this).find('A')[0].click();
return false;
});

Unbinding click handlers on certain classes

I have a table cell that I want to be able to be clicked. But once it is clicked it will have the class ".clicked". When any table cell has this class, I do not want to be clickable. Is there any way to do this?
Also you can use .off() method to unbind any events.
$('td').on('click', function(){
$(this).addClass('clicked').off('click');
})
This is easily done by inserting some code at the top of the event handler.
$('td').click(function(e) {
if ($(this).hasClass('clicked')) {
e.stopPropagation();
e.preventDefault();
return;
}
// the rest of your code here
});
The other, more involved, way, is to use event delegation and only fire the function if the element doesn't have the class when it's clicked:
$('table').on('click', 'td:not(.clicked)', function() {
// your code here
});
You can use the .one() event registration method to do it, it will un-register the handler once the event is fired.
$('td').one('click', function(){
console.log('hanlded', this)
})
Another solution could be
$('table').on('click', ':not(.clicked)', function(){
console.log('hanlded', this)
})
Just add :not(.clicked) to whatever selector you're using to exclude clicked cells.

stopPropagation() but keep other click events active

I have some jQuery code to close my menu:
$('body').click(function(){
$('#menu').hide();
});
$("#menu").click(function(e) {
e.stopPropagation();
return false;
});
However on my #menu element I have some elements that have click events attached to them:
$('.menu_elem').live("click", function(){
//Do something
});
My problem is that the e.stopPropagation() is preventing my click events for my menu. How can I avoid this?
You can use e.target to include or exclude the element in condition for stopPropagation,
$("#menu").click(function(e) {
if(e.target.id == "menu")
{
e.stopPropagation();
return false;
}
});
I'm not sure I understand your HTML exactly, but if #menu is the root of your menu and .menu_elem are menu items in the menu, then you could change your delegated event handling to to be captures at #menu like this:
$("#menu").on("click", ".menu_elem", function () {
// do something
});
This has the following advantages:
It changes the delegated event handling to capture the bubbled events at #menu so it doesn't need propagation beyond #menu in order to work so you don't have to change your.stopPropagation() code.
It switches away from (the now deprecated) .live() to the replacement .on() which gives you more control over where the bubbled events are captured.
Write up your code that you want to execute and call it before you call stopPropagation..
$("#menu").click(function(e) {
// Your code here
e.stopPropagation();
return false;
});
Or use the target attribute to contain your click event

Categories