Why are event handlers being removed in this function? - javascript

I'm trying to understand why event handlers need to be removed. I'm a beginner developer, and I searched all over for the answer but couldn't find the reason.
I came across the code below and I see that the event handler is removed as soon as it is bound. Is it a good practice?
bindSubmitEvent: function() {
var self = this;
$('#submitBtn').on('click', function() {
$(this).off('click', self.bindSubmitEvent);
self.validateForm();
if (self.options.valid_selection) {
self.submitForm();
} else {
$('#submitRegistrationBtn').on('click', self.bindSubmitEvent);
console.log("not valid");
}
});
}

The code you've shown does not work.
this inside the inner click handler is #submitButton, therefore trying to detach the self.bindSubmitEvent from it makes no sense, as that function was never attached to it.
Also $('#submitRegistrationBtn').on('click', self.bindSubmitEvent) will cause more trouble, as two events will be attached at the next click, and the second one will be called with this (and therefore self being the #submitRegistrationBtn. That will probably cause the whole code to fail (silently).
Is it a good practice?
Non working code is not a good practice, no. Removing an event listener sometimes makes sense (e.g. if the form was submitted, you want the registration button to be disabled), in most cases however, it is way easier to just remove the button itself (as it serves no functionality without a listener attached).

Related

Should I remove event listeners in jQuery?

My site works on jQuery + AJAX and has the only javascript file, which loads once when a user opens any page, so I'm used to add event listeners to all elements like: $(document).on(...).
In a while I'd noticed that there are too many .on(...) in the code, and I got afraid. I'd taken 9 pills and forced it to delete useless listeners every time when a user click on a link / back button.
function page_reload(){
if(c.r == 'http://example.com/page1'){
$(document).on('click', '#send', func.send);
$(document).on({mouseenter: func.me, mouseleave: func.ml}, '#chan');
}else{
$(document).off('click', '#send');
$(document).off('*', '#chan');
}
}
So is there any sense? Maybe a big number of listeners do some bad thing I don't know about?
When you attach a listener to an event, it does take memory and it can (if totally unchecked) cause memory related issues. In my experience, it is best to employ cleanup methods in your objects that, when a certain event fires, you use your .off() methods to unregister your event listeners.
The particular logic to these types of methods will vary depending on your project but something of the form:
var MyApp = {
cleanup: function cleanMyApp(event) {
this.off('#myId1', myMethod1);
this.off('#myId2', myMethod2);
}
}
$('document').on('ready', function() {
$(document).on('importantEvent', function(event) {
event.preventDefault(); // if you need to
MyApp.cleanup();
});
// or
$('#elem').on('something', MyApp.cleanup);
});
So yes, having too many listeners registered at a time can cause issues but you can monitor memory usage with your browser's dev tools and the like. In particular you can run out of stack (and heap?) memory and possibly crash the browser.
There is also a great answer here on dealing with these kinds of issues.

Excessive Use of jQuery's preventDefault() and stopPropagation()

I was recently in a discussion with a work colleague about some differences in our coding practices, where I raised an issue about his excessive use of the two above mentioned methods in his event handlers. Specifically, they all look like this...
$('span.whatever').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
/* do things */
});
He has made the claim that this is a good practice with no foreseeable blowback, and will improve cross-platform support while reducing unexpected issues down the road.
My question to you guys: If he's right, why hasn't the jQuery team implemented this behavior globally applied to all event handlers? In effect, my assumption is that he's wrong simply because the methods are available for independent use, but who knows... Your insight is much appreciated.
--
Update: I did a simple speed test, and there is a little drag caused by these two function, but nothing terribly noticeable. Still, among other things, a good reason to be deliberate in their use I think.
$('span.whatever').on('click', function(e) {
var start = new Date();
for (i = 0; i < 999999; i++) {
e.preventDefault();
e.stopPropagation();
}
console.log( new Date() - start );
});
The above logged ~9.5 seconds as is, and ~2.5 seconds when I took the function calls out of the loop.
I don't do the same thing as your colleague (pushing the 2 calls on EVERY event handler), but I do have the same practice of using these calls explicitely rather than a "return false;", and I believe that has made my life easier.
When I started with Jquery, I figured if I need to both stop propagation, and prevent default, I should just "return false", which I kind of did all over the place.
$('a.whatever').on('click', function(e) {
do_stuff();
return false;
});
But there was 2 problems I enventually encountered:
if do_stuff() has any critical error causing an exception, "return false;" will never be reached!!! The error will eventually be "nicely" swallowed by jquery; your event will bubble, and let the browser execute the default action. If you are in a single page app and a link was clicked, for all you know the page navigated away, and the entire app state went down the toilet (I've been there before).
I was too lenient with my return false: in many cases, I just needed a preventdefault(). "return false" was killing event bubbling and sometimes hindered my ability to perform another action higher up the dom hierarchy (or made some other plugin/libs I was using not work properly)
So I now prefer to be explicit. I litterally never use "return false;" any more. If I have an event handler that must either not propagate or not execute default, I deliberatly put that in my function FIRST, before any processing code. Whatever happens during event handling should NOT affect the fact that I do NOT want the default action to run, and/or event to not bubble.
And yes, that being said, I am also mindful of using just one of the 2 when required (or none at all in some cases). I do not just add both preventDefault() and stopPropagation() for no reason. Everywhere I manipulate an event in a handler, it is part of a conscious case-by-case decision.
It would be a problem if the element is part of a menu and the click event was supposed to bubble out and tell the menu to close itself too.
Or if a menu was open elsewhere and clicking outside the menu was supposed to bubble up to the body where an event handler would close the menu. But the element having stopped the bubble, prevents the menu from closing.
<div id="wrapper">
<div id="header">header
<div id="footer">footer
<div id="content">click this!!!</div>
</div>
</div>
</div>
$("#wrapper div").click(function(){
console.log( $(this) )
});
Please try clicked to div and show console...
And now added
$("#wrapper div").click(function(e){
e.stopPropagation()
})

Memory leak with unused event handler in javascript

I'm in a situation where I make a lot of ajax calls to change the same portion of html. This html represent a grid. After changing the html in the ajax call, I attach a event handler to an event of the grid. When the user click on a refresh button, I execute the same ajax call that set new html code and also add again an event handler to listen of event of the grid.
I want to know if each time I refresh my grid and add a new event handler if the previous event handler is still in memory and if yes, what are the bests practices in this situation? (e.g. unbind the event handler if exist before putting new html)
Here is an example of what I do:
$.get(this.config.actionLoggingUserListUrl, viewModel, function (data) {
MyNamespace.ui.hideGridAnimation($("#LoggingActionUsersList"));
if (data.success) {
$("#validationSummary").html("");
$("#usersList").html(data.result);
$("#LoggingActionUsersList").click(function() {
console.log("Here is my event handler attached multiple times!");
});
}
else {
$("#validationSummary").html(data.result);
$("#usersList").html("");
}
});
Note that the event handler I'm talking in this case is:
$("#LoggingActionUsersList").click(function() {
console.log("Here is my event handler attached multiple times!");
});
Event handlers stack, so yeah, this is a memork leak. Probably a fairly insignificant one, but its more the principle than the effect. Unless for some reason you really do need to have dynamic event handlers (something that is pretty rarely used as there aren't very many realistic uses for it), I'd strongly suggest pulling the event handler assignment out of the ajax call.
If you do need the event handler to change, the clever way to do it would be to make your event handler smart enough to know a little bit about the object to which it is assigned. That way, instead of adding a new event each time, you can just have logic in the event handler do different things based on the current identity of the object.
why are you binding it every time you make the call?
You are adding onto the stack every time. You are not replacing it. Best solution is to use on and do it once.
Other solution is to unbind the click event, before you add click event. The problem with this solution is if anything else added the click event, you just removed it.

JavaScript - All onClick events are not working?

Alright, so I'm making a Facebook-style chat. But that doesn't really matter.
See here:
http://jsfiddle.net/SkHme/7/
Nice and pretty, right? Well, there's a problem. Notice this line:
<div class="conversation EmperorCuzco" onclick="setActive('EmperorCuzco')">
See the onclick attribute? Well, it's not working. However, I have confirmed that the function itself DOES work. (if you run it just like that in the JavaScript, it runs like a dream) I have further confirmed that the function is not the problem by attempting to replace the onclick value with a simple alert('blah'), but that doesn't work either.
So, what's up? I'm guessing that something in my JavaScript is somehow disabling something, but I have absolutely no idea what it could be, nor how I could go about fixing it. I did some web searching, but couldn't find anything that helps.
What's going on?
Your setActive function is defined inside the scope of the $(document).ready handler. Move the function outside that function so that it is in the global scope.
Right now it looks like this:
$(document).ready(function()
{
// ...
function setActive(new_conversation)
{
// ...
}
});
Now change that to:
$(document).ready(function()
{
// ...
});
function setActive(new_conversation)
{
// ...
}
Really though, you should separate your content from your interactions and bind those event handlers in your script itself. Something like:
// Refers to the last clicked conversation *button*
// Initialize to empty jQuery object
var $active_conversation = $([]);
// Binding on #chat, targeting click events on .conversation_button children
$("#chat").on("click", ".conversation_button", function() {
// Hide currently active conversation
$active_conversation.hide();
$active_conversation.siblings('.conversation_button').removeClass("selected");
// Set as currently active conversation button
// Note: this refers to the clicked <div class="conversation_button">
var $this = $(this);
$active_conversation = $this.siblings('.conversation');
// Show this conversation
$active_conversation.show();
$this.removeClass("alert").addClass("selected");
});
Some advantages of this approach:
You don't need different classes for different conversations. By storing the actual conversation DOM element (as a jQuery object) in $active_conversation, the conversation can be identified without any extra processing.
You can add and remove whole list items with a conversation without assigning new event handlers. In the sample above, the event handler for all .conversation_button elements is defined at the level of #chat. For more about this binding mechanism, read up on .on (or .delegate for pre-1.7).
Here, have an updated fiddle! :-)
If all you say is really true (bad mistakes happen), the only thing that can make this is that an other event handler which takes your event before uses stopPropagation() or return false;
A quick check that can do is replace onclick with onmousedown or onmouseup, and see if you alert become visible.

Jquery/Javascript

Good morning peoples.
I have a bit of a slight jQuery question/problem that I am not sure how to tackle.
I have a click handler bound to varying classes on some anchor tags (which works great). I have now come to a page that needs an extra handler on the same anchor tags so I decided to use some namespacing to get the desired result. The trouble with the namespacing is that it is called before the original click handler and creates problems with the first handler. The error is raised due to the first handler requiring an element to exist to continue in the function but the namespaced click handler removes the element before so it errors out.
Does anyone know if one can tell namespaced handlers to execute After the original handler or would I have to completely re-write the script and perhaps extend it on this one (and only) page to have the funcitonality work as I would like.
Thanks in advance.
Alex
It's easier to add a classname to the anchors on the page that events are bound on and check that in my function..
Sorry for any time wasted
If you bring the handler out into a separate function, you can call the original handler from the other handler.
function handler() {
// original event handler code
};
$('#originalTarget').click(handler);
$('#otherTarget').click(function() {
// code to do anything specific to this handler
handler();
}
You can assign more than one handler:
// general handler
$('a.linkclass').click( function(){
doThis();
});
// specific handler on the page in question
$('#specificlink').click( function(){
doSomethingExtra();
});

Categories

Resources