Fire JavaScript event on object creation - javascript

Is there any way to fire an event when an object of a certain type is created?
I have an <div> element with editableContent="true" and when the user presses enter within the <div>, a new <div> is created that takes up just that line. Is it possible to have an event fire whenever a <div> object is created within my original <div> object?
I know one way to do this would to listen for keystrokes and on an 'enter' key being pressed, do a bunch of logic to figure out what to do, but this seems a lot less elegant - so it would be great if there was another way.

There are the a bunch of DOM Events and some of them will be supported by firefox for example. But I dont think that IE will support only one of them. Here is a complete list.
First you can fire a custom event every time you create a new div, or you have a settimeout that checks every second if the count of you divs childnodes has changed.

You have a function that creates a div when you press enter. Why don't you just add a function call at the end of it.
function createDiv(){
//create div
//append div
divCreated();
}

There's no default callbacks for new elements creating. The first thing I have in mind - you can add an event listener for mouseUp event and check content delta (changed part) - if it looks like an element markup with regexp.

Everybody who looking for a way to react to changes in a DOM should take into consideration MutationObserver. It is a standard DOM4 feature currently implemented in all latest (even not all modern but latest!) browsers.

Related

JavaScript Understanding the need of event delegation

I read in one of the articles on the web this sentence:
Adding event handlers to each of the 1000 cells would be a major
performance problem and, potentially, a source of browser-crashing
memory leaks.
But I don't understand it. If for example, I am doing something like this:
const handler = e => // do something
document.querySelectorAll("td").forEach(td => {
td.addEventListener("click", handler);
});
I don't create 1000 functions, they all use the same reference, so what I am missing? What is the problem?
I don't create 1000 functions, they all use the same reference, so what I am missing?
You would still create 1000 function references in the 1000 DOM nodes. This might not seem significant, but the table could grow…
Of course, you are basically right, if you don't create 1000 closures there won't be as many problems. However, do you really have 1000 buttons that all do the same? With closures, it's easy to let them do marginally different things; with only one function you would need to make it dynamically depend on the cell that was clicked (the event target). And when you're doing that already, you could just go ahead and use only a single function reference and event delegation…
The way event delegation works is that you add an event listener in a parent element. When the parent triggers the event, you get a reference of the actual element that triggered the event and then check if that is the element you want to listen to
Maybe a code would help you understand it better
// Listen for the event on the parent...
document.getElementById("event-table").addEventListener("click", function(e) {
// '.target' will now give us our actual clicked element
if(e.target && e.target.nodeName == "td") {
// Wooho, a '<td>' node was clicked
console.log("We have our click event delegated!");
}
});
The way it helps is that now, instead of adding a eventListner on each element I can simply attach it to a parent and then check for it's child.
The real use of this functionality is when you want to attach events on some dynamically created elements. Say a row in your table is added dynamically after a database call. In such case, you would have to reattach a event listener to the new node if you're going the route of attaching the event to each node. But if you're using the delegation approach, as the event is attached to the parent, the newly created child node automatically becomes a part of the event
It can prove to degrade performance if you attach it to a parent that has many child's, which don't actually are the intended nodes for the event. For eg, if you attach the body node itself and then delegate to the 'td' tags inside it ! In theory it would work for sure but would potentially slow things down.
In JavaScript, events will bubble up the DOM chain. When you click on td, any event handler on td will fire, and also on tr and on table, etc., all the way up the chain.
The article is telling you to register the event on table (for example) instead of each td element.
I don't create 1000 functions, they all use the same reference, so
what I am missing? What is the problem?
You are not creating 1000 functions but you are registering 1000 event handlers, which is what the article is advising against. Register one event handler on some parent element, like table, instead.
Here is a jsFiddle example on how to register one event handler for 1000 table cells.
Maybe an analogy will help you better understand it. Say you have a company where customers call you and you send them something.
Attaching a unique event handler to each cell would like having 1000 phone lines, one for each customer and having to hire 1000 employees to man each phone waiting for orders.
Attaching a single event handler to all of the cells is like still having 1000 phone lines, one for each customer but monitored by a single employee who has to figure out which customer's line is ringing and where to send the stuff. You no longer need 1000 employees but you still have 1000 phone lines.
Using event delegation is like having a single phone line that all your customers call and a single employee who know where to send their stuff based on the caller id.
Using a single phone line makes your employee much more efficient.

How to unbind all attached events on a given DOM in JavaScript? [duplicate]

This question already has answers here:
Javascript/DOM: How to remove all event listeners of a DOM object?
(18 answers)
How to remove all listeners in an element? [duplicate]
(3 answers)
Closed 7 years ago.
Say some DOM node (like a <a> tag) is bind with some events, like click to open a popup or anything. But I can't read the code which contains the binding stuff. How do I unbind them all? Is there any simple way/hack?
update again:
--
I checked the Elements > Event Listeners with my Chrome and found that there is a click event handler on my a, to make sure I am right, I tried the following steps:
First, I manually added a click handler from the console like this:
$a.click(function(){console.log(1)});
And I go back to the listeners tab, hit the refresh button, there is one more (now two handlers).
Then I use:
$a.off();
And I go back again, the one that I manually added is gone, remains only one handler (the one I want to git rid of)
And I used the replace-node way, and go back again, I found that the click handler is totally gone. That is what I want.
So, it's like $a.off() is not working, even though it's pretty sure that the event is bind to the element itself, not its parent nodes.
--
update:
I am using jQuery
I tried $.unbind() and $.off(), neither works
I used $('#myEl').replaceWith($('#myEl').clone()); and this works fine. But it seems this way is not very perfect.
You can create a clone of the element and replace it. Cloning an element will not clone the attached event listeners.
var original = document.getElementById('id'),
var clone = original.cloneNode(true);
original.parentNode.replaceChild(clone, original);
There is no API to do that. Depending on which modifications have been made to the element, you could clone the node, move the children and replace the original with its clone:
var clone = element.cloneNode();
while (element.firstChild) {
clone.appendChild(element.lastChild);
}
element.parentNode.replaceChild(clone, element);
cloneNode will copy all the attributes, but not any changes that have been made to DOM properties.
Maybe you can use jquery .unbind():
$('a').unbind()
In new version jquery, .off() is suggest to use.
$('a').off()
if this not working, it means your event is not binding on this element but others like parent element, you may have this code in your script, and you have to decide how to deal with that:
$('.parent').on('click','a',callback);

Handle stuff after dom changes

I've got a page with some Javascript / jQuery stuff, for example:
(function()
{
$('.tip').tooltip();
$('.test').click(function()
{
alert('Clicked!')
});
}();
On the page I insert some HTML with jQuery so the DOM changes. For example, I insert a extra element with the class "tip" or "test". The just new inserted elements doesn't work because jQuery is working with the non-manipulated DOM and the just inserted elements aren't there. So I've searched around and came to this solution for the "click":
$('body').on('click','.click',function()
{
alert('Clicked!')
});
I don't understand why, but this way it's working with the manipulated DOM and the jQuery stuff works on the new inserted elements. So my first question is, why does this work and just the click() function not? And the second question, why do I have to point to the "body"?
Finally, my third question is, how get this done with the tooltip?
I know that there is so many information about this subject (previous the delegate() and live() function I've read) but I can't found a explanation about it. And I can't get my third question solved with the information I found.
I'm looking forward to your responses!
Extra question:
4) Is it recommended to point always to the "body" for this kind of situations? It's always there but for possible performance issues?
So my first question is, why does this work and just the click()
function not?
Because the event handler is now delegated to a parent element, so it remains even after replacing/manipulating child elements.
Ancient article on event delegation for your perusal - but the concepts remain the same:
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
And the second question, why do I have to point to the "body"
You don't, any suitable parent element will do. For example, any direct parent (a div wrapper, for instance) which does not get replaced.
Finally, my third question is, how get this done with the tooltip?
You need to re-initialize your tooltip plugin on the newly inserted elements. For example:
$.get("foo.html", function (html) {
$("#someDiv").html(html);
$("#someDiv").find(".tip").tooltip();
});
The click() event doesn't work when you manipulate the DOM because JQuery is not watching for DOM changes. When you bind the click() event it is selecting the elements that are on the page at that time. New ones are not in the list unless you explicitly bind the event.
Because you have pointed the click() event on the body. JQuery then checks to see if the target of the click matches any of the event handlers (like what you have created) match the element clicked. This way any new elements will get the event 'associated' with them.
Because the tooltip isn't an event that you can place on the body, you will need to re-initialize it when the element is created.
EDIT:
For your fourth question, is it depends. The advantage of binding to the body is that you don't accidentally bind an event to an element more than once. The disadvantage is that you are adding event handlers that need to be checked on each event and this can lead to performance issues.
As for your concerns about DRY, put the initialization of the tooltips into a function and call that when you add them. Trying to avoid having the same function call is a little overkill in this regard, IMO.
Events are bound to the specific object you are binding it to.
So something like $('.tip').tooltip() will perform the tooltip() functionality on $('.tip') which is actually just a collection of objects that satisfies the css selector .tip. The thing you should take note of is, that collection is not dynamic, it is basically a "database" query of the current page, and returns a resultset of HTML DOM objects wrapped by jQuery.
Therefore calling tooptip() on that collection will only perform the tooltip functionality on the objects within that collection, anything that was not in that collection when tooltip is called will not have the tooltip functionality. So adding an element that satisfies the .tip selector, after the tooltip() call, will not give it the tooltip functionality.
Now, $('body').on('click','.click', func) is actually binding the click event to the body tag (which should always exist :P), but what happens is it captures whether the click event has passed through an element your target css selector (.click in this case), so since the check is done dynamically, new elements will be captured.
This is a relatively short summary of what's going on... I hope it helped
UPDATE:
Best way for your tooltip thing is to bind tooltip after you have added elements, e.g.
$('#container').load('www.example.com/stuff', function() {
$('.tip', $(this)).tooltip();
});

JQuery click anywhere except certain divs and issues with dynamically added html

I want this webpage to highlight certain elements when you click on one of them, but if you click anywhere else on the page, then all of these elements should no longer be highlighted.
I accomplished this task by the following, and it works just fine except for one thing (described below):
$(document).click(function() {
// Do stuff when clicking anywhere but on elements of class suggestion_box
$(".suggestion_box").css('background-color', '#FFFFFF');
});
$(".suggestion_box").click(function() {
// means you clicked on an object belonging to class suggestion_box
return false;
});
// the code for handling onclicks for each element
function clickSuggestion() {
// remove all highlighting
$(".suggestion_box").css('background-color', '#FFFFFF');
// then highlight only a specific item
$("div[name=" + arguments[0] + "]").css('background-color', '#CCFFCC');
}
This way of enforcing the highlighting of elements works fine until I add more html to the page without having a new page load. This is done by .append() and .prepend()
What I suspected from debugging was that the page is not "aware" of the new elements that were added to the page dynamically. Despite the new, dynamically added elements having the appropriate class names/IDs/names/onclicks ect, they never get highlighted like the rest of the elements (which continue to work fine the entire time).
I was wondering if a possible reason for why my approach does not work for the dynamically added content is that the page is not able to recognize the elements that were not present during the pageload. And if this is a possibility, then is there a way to reconcile this without a pageload?
If this line of reasoning is wrong, then the code I have above is probably not enough to show what's wrong with my webpage. But I'm really just interested in whether or not this line of thought is a possibility.
Use .live to "Attach a handler to the event for all elements which match the current selector, now and in the future". Example:
$(".suggestion_box").live("click", function() {
// means you clicked on an object belonging to className
return false;
});
Also see .delegate, which is similar.
Since the .live() method handles events once they have propagated to the top of the document, it is not possible to stop propagation of live events. Similarly, events handled by .delegate() will always propagate to the element to which they are delegated; event handlers on any elements below it will already have been executed by the time the delegated event handler is called.
from the jQuery documentation =)
(only to explain better why #karim79 also suggested the delegate method ;P )

How do I register a Javascript event handler to an element that hasn't been added to the page yet

I'm trying to build a greasemonkey script which will dynamically create tables of data based on user interaction with... other dynamically created tables of data. My problem is that I'm having to make two passes every time I create a table: one to create the table, and another to go grab all of the objects in the table I want to add event handlers to (by id) and add the various event handlers to them.
If I attempt to, say, add an onClick event to a table td before I've created the table and inserted it into the HTML, I get a "component is not available" exception.
This is incredibly cumbersome, because I either have to maintain, separately, a list of the ids and what I should do to those elements when I make my second pass to add the handlers, or develop a naming convention by which I know, based on the id, what I should do with the element.
There HAS to be a better way to do this. I just haven't figured it out yet. Anyone have any ideas?
Firstly, I'd love to know why you need a different ID for every single TD. Is the ID holding important information, such as an index? In this situation it might be better creating each TD within a loop. Also, obviously you can't attach an event handler to a DOM element which doesn't exist! It doesn't have to be injected into the DOM but it DOES have to exist in some capacity.
jQuery's live() isn't a magical mystery, it just uses event delegation, so it attaches the event to a parent element, such as the table and then decides what happens dependent on the target of the click. Here's a rudimentary example. I register a handler to the 'body' element, and then I test each time to see what the target is, if it's a TD element I doSomething() ->
document.body.onclick = function(e) {
var realTarget = e ? e.target : window.event.srcElement;
if ( realTarget.nodeName.toLowerCase() === 'td' ) {
doSomething();
}
};
Event delegation relies on something called event bubbling (or "propogation") which is the way in which modern browsers implement the event model. Each event, when triggered will travel upwards through the DOM until it can go no further. So if you click on an anchor within a paragraph the anchor's 'click' event will fire and THEN the paragraph's 'click' event will fire etc. etc.
jQuery 1.3+ has a new live() function that can set up event handlers for elements that don't exist yet .. check it out
You have to wait for the element to be added to the page, then add the event handler then.
There is no easy way to say "add this to all elements of this type, now and in the future".
It is possible to have a timer periodically check the page for new elements, applying a queue of events (or other properties) to them as they appear, all behind the scenes. This can be abstracted out and re-used, for example Jquery can do that sort of thing.
As JimmyP pointed out, your problem can easily be solved using event bubbling. You might consider writing a wrapper function to work around browser inconsistencies - my own version can be found here and would be used like this:
capture('click', '#element-id', function(event) {
// `this` will be the originating element
// return `false` to prevent default action
});

Categories