removing onClick from dynamic elements - javascript

Should I be removing dynamic clickHandlers to the dynamically created html tags or is it taken care of automatically by the garbage collector.
My primary browser is Safari (embedded in iOS app), but I think i read that IE has memory leak problem around this.
var li = document.createElement('li');
li.addEventListener('click', function(){});
so if this element was removed from DOM later on, should I delete the clickHandler, just in case please let me know how to property delete a clickHandler?

li.removeEventListener("click", function_name);
This will remove the event listener
Source: http://www.w3schools.com/jsref/met_element_removeeventlistener.asp
For anonymous functions, the point of them is really to have no reference and lacks a name, so for removeEventListener() you will need a named function.

If there are no references remaining to the element after you remove it, the GC should clean up handler functions that it references. So you shouldn't need to do anything.
But if IE has a bug along this way, you can use removeEventListener to remove the handler, but this requires that you use a named function, since you have to give the same function to removeEventListener as you did when you called addEventListener, and anonymous functions will never be the same as each other.
function myClickHandler {
...
}
var li = document.createElement('li');
li.addEventListener('click', myClickHandler);
...
li.removeEventListener('click', myClickHandler);
myClickHandler = null;
li.parentNode.removeChild(li);
myClickHandler = null; is needed because otherwise the function name will hold a reference to the handler function, so it won't be GCed.
If you have multiple LIs, and they're all using the same handler function, it shouldn't be necessary to do this. No matter how many LIs you have, they're all just referring to the same function, so it doesn't take up lots of memory.

Related

How does the onmouseover attribute know to accept an event object?

I was looking at this basic example below (which makes all images in the DOM semi-transparent on mouseover), and was confused as to how an arbitrary function, such as handleMouseOver, receives an event object if you give it an argument.
How is it that the act of assigning such a function to the onmouseover attribute tells it to modify this function in this way, as there's nothing inherent in the function definition itself that says: "please pass me an event"? Is the assignment operator being overloaded somehow? Or is the browser doing some extra work here? I would really appreciate a link to a detailed explanation of this phenomenon because it doesn't seem to make any sense looking at it as pure JavaScript (to me at least!)
function handleMouseOver(e) {
e.target.style.opacity = 0.5;
}
function handleMouseOut(e) {
e.target.style.opacity = 1;
}
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
elements[i].onmouseover = handleMouseOver;
elements[i].onmouseout = handleMouseOut;
}
Lets break it down by taking one browser's example. IE'S OnMouseOver Event for instance.
In the remarks section it says it passes IHTMLEventObj for ALL events even for the events that don't require it such as Body.OnLoad.
When we go into IHTMLEventObj's detail, we read the following remarks
Although all event properties are available to all event objects, some properties might not have meaningful values during some events
So, Event object is passed regardless; you have to access the object in some specific events and get event-specific properties to get event-related data.
onmouseover, for example, is an event handler. When the event handler needs to be called (in this case when the browser javascript engine decides it) then it will call it be passing it some pre-determined arguments (all good documentation will explain what those arguments are). Your use of those arguments is optional however.
This can be demonstrated with a manual function call like so:
function myFunction(e){
alert(e.myProperty);
}
//assign the handler
var handler = myFunction;
//when required, create event parameter data and call the function assigned to the handler
var myE = { myProperty: "some data" };
handler(myE);
It is not "exactly" how it works (because I don't know how browsers have chosen to implement their code), but it shows the concept.
Here is an example in action
Not only the event object is passed, but also the this value within the function is set to the event target. This is done by the browser, and dictated by the DOM specification.
EDIT:
I was hoping to find something more detailed in the DOM specification (I'm sure I've seen that before), but so far I found this:
In JavaScript, user-defined functions are considered to implement the EventListener interface. Thus the Event object will be provided as the first parameter to the user-defined function when it is invoked. Additionally, JavaScript objects can also implement the EventListener interface when they define a handleEvent method.
https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#glossary-event-handler
By the way, the last sentence is talking about an interesting way to bind event listeners, in an OO context.
You can pass whatever arguments you like to any JavaScript function.
Defining them in the function definition just means you have a named, local variable to access them with.
That is to say:
function foo() {
}
foo("hello");
… won't throw an error.
When a function is treated as an event handler (which is what code provided by the browser will do if you assign a function to the onmouseover property of a DOM node) then the event object will be passed as an argument.

Is "remove" a reserved keyword in Google Chrome?

I have an interesting problem, and I think I got to the root of it, but I wanted to be sure. I have a link that calls a function called remove(). All browsers except Chrome had no issues with the function. However, the link that is clicked disappeared in Chrome, even when I simplified the function as in the example below. I have seen this question: Can't use "download" as a function name in javascript. In the links, however, I did not see anything about "remove" as a reserved keyword. My question is this, I am correct about this being a keyword? If so, is there anywhere I can find a list of Google keywords? I have searched and have not found this to be a problem anywhere else.
Remove
Javascript:
function remove(){
alert("Hi");
}
Elements in Chrome have a .remove() method which allows for self-removal of an element instead of having to do it from the parent.
The trouble is that when using attribute handlers, you get a different scope chain. That scope chain includes the element itself, as well as the document. This means that all properties of the element and document show up as variables.
Because you named your function remove(), and because it's a global function/variable, it is being shadowed by the .remove property (now variable) on the element itself. This can be seen with an alert. If you change your handler to:
onclick="alert(remove)"
...you'll get something like:
function remove() { [native code] }
So it's not that it's reserved, but rather that it's used as a property which ends up shadowing the global.
To fix it, either use the global directly:
onclick="window.remove()"
Or change the function name.
I can't find any documentation on it, but DOM elements in Chrome have a native method remove that apparently removes them. In onclick, this actually refers to the element itself so it ends up calling this.remove() which removes the element. To get around this, you can just call window.remove() instead.
http://jsfiddle.net/3YkZH/1/
It would also be better to use standard event binding via addEventListener which does not have this problem when simply calling remove:
http://jsfiddle.net/3YkZH/2/
I had no issue in chromium using it, well not in this manner
Remove
function remove() {
alert("Hi");
}
document.getElementById("remove").addEventListener("click", remove, false);
on jsfiddle
Inline javascript is considered bad practice.
If you have more elements using the same function, just add more lines, like this
document.getElementById("remove1").addEventListener("click", remove, false);
document.getElementById("remove2").addEventListener("click", remove, false);
document.getElementById("remove3").addEventListener("click", remove, false);
document.getElementById("remove4").addEventListener("click", remove, false);
or you could get a nodelist and loop through that
var nodelist = document.querySelectorAll("[id^=remove]");
Array.prototype.forEach.call(nodelist, function (element) {
element.addEventListener("click", remove, false);
}
You can take a look at another answer here on SO to find out more about the differences between event binding methods, also do a little G searching on the subject will give you further information. And of course, you would have avoided the issue that you were experiencing by doing it in this manner.

removeEventListener with Unique Anonymous Function

I have an object that generates HTML elements that are also connected with an array of the object, and let us say we have one instance of it. So as it creates the elements it also assigns the following event listener to a nested part of the element (the class being uploadDelete).
Now this event listener needs to call the delete method of the instance of the object that created it, with the value of i assigned at its creation. Because events are under Window, the instance needed to be passed to an anonymous function along with the i value.
This therefore assigns a very unique function to the event, and because the delete method will be destroying the element containing the listener I would like to remove it first; from what I've read it could cause leaks otherwise(?). I'm also using Strict Mode, so not arguments.callee.
file.display.getElementsByClassName('uploadDelete')[0].addEventListener('click',
(function(that,i){
return function() {
that.delete(i);
};
})(this,i), false);
I've tried many different things, but when I started having an anonymous function inside of a function inside of a function which is then called in the listener, I figured I should just ask on here. I might have a solution to the overall problem, changing other code, but it would still help if this could be answered.
Here is what I ended up doing, with the help of Norguard's answer. Since the uniqueness was stemming from an object called file, I just created a new property of file to store the function:
file.deleteFunction = (function(that,i){
return function() {
that.delete(i);
};
})(this,i);
file.display.getElementsByClassName('uploadDelete')[0].addEventListener('click',file.deleteFunction, false);
The delete function that is called then removes the event listener.
A relatively-painless way of doing this might be to create an object in the scope that's responsible for adding and deleting listeners, which builds an ID, serial or non, and will store whatever the listener is in an object, with that ID, returning the ID to whichever object/module requested it (or passing the anonymous function back to them).
// trivial example
var listeners = {},
i = 0,
add = function (context, func, closure) {
var enclosed = (function (closure) {
return function () { /* ... */; func(); };
}(closure)),
index = i;
context.addEventListener("...", enclosed, false);
listeners[index] = enclosed;
i += 1;
return index;
};
add will now add your listener, but will also store the function that you're passing into addEventListener into the listeners object.
If you need a reference to i, you've already got it in the closure, if you want it.
So now when you remove stuff, you can just look for the function saved at listeners[i].
An alternate, if you don't want to save a table full of these in one spot, for whatever reason, would be to catch the return statement, and instead of returning i, return the function;
// inside of your module
// I'm not usually crazy about `this`, without doing something particular with it,
// but hopefully it illustrates my point
this.cached_func = add(this.el, this.callback.bind(this), this.secret);
So now, when it comes time to delete everything, and you want to shut down your listener...
remove(this.cached_func);
All of that said, the leaks that you've read about are still possible, but the major culprit was IE6/7 (and earlier).
As people steer further from bad browsers, this becomes less important.
In fact, encouraging memory-dumps in IE6 is probably just a good way to encourage people to not use IE6.

When binding event to multiple elements at once, new instance for each element?

I have 100 BUTTONS in my page ( each of them has class='btn').
Also I have a single button which prepare all the other 100 buttons.
<input type='button' onclick='bindTheClickevent()' />
When pressed, - it calls bindTheClickevent() - (which binds the click event to all 100 others).
In the Script Section I put:
function bindTheClickevent ()
{
$(".btn").bind('click',function () {
$(this).css('color','red');
});
}
Questions
1) In memory, how many instances of the anonymous function are created?
2) In memory, Does the bindTheClickevent() function will ever be free (GC)? - please notice that the Bind is called inside the bindTheClickevent function...
3) When, eventually - the bindTheClickevent function will be free to GC ?
Lets make a change
function bindTheClickevent ()
{
$(".btn").bind('click',function () {
changeColor($(this));
});
}
function changeColor(obj)
{
$(obj).css('color','red');
}
Now - after the change
1) Is there any difference if I Do that?
2) In memory, how many instances of the anonymous function are created?
3) Does the bindTheClickevent() function will ever be free (GC) ? - please notice that the Bind is called inside the bindTheClickevent function...
"1) In memory , how many instances of the anonymous function are created ?"
Which anonymous function?
For the inline onclick, you get a function assigned to the onclick property of the element like this:
function(event) {
bindTheClickevent();
}
... or similar depending on the implementation. That function will be free for GC when the element is dereferenced or the function is dereferenced from the onclick property.
With respect to the jQuery code:
$(".btn").bind('click',function () {
$(this).css('color','red');
});
...while the anonymous function is shared, what you don't see is that if the elements in question do not already have a jQuery handler bound, jQuery will internally create a unique function for each element.
That internal handler is what actually gets bound to the element, and when the element receives an event, that handler is invoked, analyzes the event, and invokes the handler you originally passed (if necessary).
This means 100 jQuery bound elements equals 101 unique function instances.
In order to make sure that any handlers bound using jQuery are GC'd, you need to make sure that you always use jQuery to remove DOM elements. If you don't, all the data (including handlers) stored in jQuery.cache doesn't get cleaned up, and so they'll always be referenced via the global jQuery namespace.
EDIT:
Given that there are 100 elements that have the class btn, that don't have any handlers bound by jQuery, then this code:
$(".btn").bind('click',function () {
$(this).css('color','red');
});
...will create 101 unique Function instances.
Why 101?
Well, what jQuery does is the first time you bind a handler to an element, it internally creates a unique generic handler for every element. This is the handler that is actually invoked when an event occurs, and handles all event types.
Your handler function is never actually bound to the element.
So that generic internal handler when invoked will analyze the event that took place, and see if any handlers have been associated with the given element using .bind() that match that event type. If so, it calls the handler that passed.
Now let's say you bind another handler:
$(".btn").bind('mouseenter',function () {
$(this).css('color','blue');
});
...since we're binding to the same elements, they already have the necessary internal handler and another does not need to be created. So all that happens is that the function you pass is referenced internally, and is invoked by the generic internal handler when needed.
As such, given the code snippets above, there now exists 102 unique Function instances.
It looks like only one instance of the function is created in both circumstances. It appears as though References to the anonymous function are attached as the event handlers for each element.
Example - Using a closure to show the sharing of scope between button event handlers.
Note that this can cause interesting behavior if you involve closures because all elements will share the same function (and closure scope).
And no, your declared functions will not be GC'd because of their global scope.
Additionally
To attach them independently (not by reference), loop over the selected elements with .each() and attach the function individually.
Example
$('.btn').each(function() {
$(this).bind('click',function() {
// each '.btn' has it's own copy of
// this anonymous function
}
});
If you do something like these:
for (someiterations)
{
$(myobj).bind("click",function()
{
// ...bla...
});
}
In this case you are creating a new function each iteration.
In your function this is not happening because you are passing the function to a parameter, so there is a place which has stored it's reference (yea the function param) that will do something like this:
for (iterations)
{
myob.addEventHandler(event, funcref);
}
So should be ok, now:
Don't think so, not sure of the syntax however.
1 as I explained
No because it's in the global scope and it's not assigned to an instance, you can think of it as a constant, not as a variable
Note: The anonymous function will not be released, it's referenced by the event handler.

Why does this Javascript object not go out of scope after $(document).ready?

I have some working Javascript that manipulates the some DOM elements. The problem is, I don't understand why it works, which is never a good thing. I am trying to learn more about object oriented javascript and javascript best practices, so the organization may seems a little strange.
Basically, I wrap two methods that manipulate the DOM inside a CSContent object. I create an instance of that object, content in $(document).ready and bind some events to the functions in content. However, I am confused as to how these functions can still be called after $(document).ready exits. Doesn't that mean that content has gone out of scope, and its functions are not available? Anyway, here is the code:
function CSContent() {
var tweetTextArea = document.getElementById('cscontent-tweet'),
tweetTextElement = document.getElementById('edit-cscontent-cs-content-tweet'),
charCountElement = document.getElementById('cscontent-tweet-charactercount');
this.toggleTweetTextarea = function () {
$(tweetTextArea).slideToggle();
};
this.updateTweetCharacterCount = function () {
var numOfCharsLeft = 140 - tweetTextElement.value.length;
if (numOfCharsLeft < 0) {
$(charCountElement).addClass('cscontent-negative-chars-left');
}
else {
$(charCountElement).removeClass('cscontent-negative-chars-left');
}
charCountElement.innerHTML = '' + numOfCharsLeft + ' characters left.';
};
}
$(document).ready(function () {
var content = new CSContent();
//If the twitter box starts out unchecked, then hide the text area
if ($('#edit-cscontent-cs-content-twitter:checked').val() === undefined) {
$('#cscontent-tweet').hide();
}
$('#edit-cscontent-cs-content-twitter').change(content.toggleTweetTextarea);
//Seems wasteful, but we bind to keyup and keypress to fix some weird miscounting behavior when deleting characters.
$('#edit-cscontent-cs-content-tweet').keypress(content.updateTweetCharacterCount);
$('#edit-cscontent-cs-content-tweet').keyup(content.updateTweetCharacterCount);
content.updateTweetCharacterCount();
});
This, m'lord, is called a closure: the local variable content will remain in memory after $(document).ready exits. This is also a known cause of memory leaks.
In short, you bind this function to an event listener of a DOM element and then the JavaScript garbage collector knows that it should keep the local variable intact. You can't call it directly (outside of the function), unless the event is triggered. With some, you can do this ‘manually’, if you really want to call the function afterward (e.g., using element.click() to simulate a click).
I assume you wonder why the event handlers like
$('#edit-cscontent-cs-content-twitter').change(content.toggleTweetTextarea);
work?
Well you don't pass content as event handler but the function that is contained in content.toggleTweetTextarea. And this reference will still exist after content does not exist anymore. There is nothing special about it. You just assigned an object (the function) to another variable. As long as at least one reference to an object exists, the object won't be garbage collected.
Now you may ask why those functions have still access to e.g. tweetTextArea ? This is indeed a closure. When the functions are created via new CSContent(), the activation context of this function is added to the scope chain of the inner functions CSContent.toggleTweetTextarea and CSContent.updateTweetCharacterCount. So even if you don't have a reference to content anymore, the scope of this function is still contained in the scope chain of the other functions.
You won't be able to access the object contained in content anymore after ready() is finished, this indeed goes out of scope.
My brain is off today, but shouldn't you be using closures in this situation?
$('#edit-cscontent-cs-content-twitter').change(
function(){
content.toggleTweetTextarea();
}
);

Categories

Resources