I'm trying to add listener to my table in order to call another jJavascript function.
My app is a simple upload script, when user chooses a file it uploads and creates new row in the table. Therefore at each creation of new row I would like to call a Javascript function.
My sample code is the following:
$("#fayls").bind("DOMNodeInserted", function() {
$(".chzn-select").chosen();
alert('ha cambiato')
});
Where fayls is the id of my table.
When I run this code, it calls this event infinitely instead of just one.
How should I solve this problem ?
Thanks.
I think the problem is in that Chosen plugin converts, among all others, elements inside that container with "#fayls" ID. Consider this:
HTML:
<div id="something"></div>
<div id="completely_different"></div>
<button id="change_something" type="button">Change!</button>
JS:
$('#change_something').click(function() {
$('#something').append($('<p>Internal P</p>'));
});
$('#something').bind("DOMNodeInserted", function(event) {
alert(event.target);
$("#completely_different").append($('<p>SOme p</p>'));
});
JS Fiddle
It works as expected (each click on the button adds a <p> element into boths divs).
Now let's change our HTML slightly:
<div id="something">
<div id="completely_different"></div>
</div>
<button id="change_something" type="button">Change!</button>
JS Fiddle
... and now the event will be fired indefinitely - because when we update the inner div, the DOMNodeInserted still bubbles up to the outer one.
Also note that DOMNodeInserted event is, well, considered deprecated now (but still its support is buggy in IE9, as described here).
You has the problem because the event will be dispatched also at the parent nodes (bubbles)
From W3C
DOMNodeInserted:
- Fired when a node has been added as a child of another node.
- This event is dispatched after the insertion has taken place.
- The target of this event is the node being inserted.
- Bubbles: Yes
- Cancelable: No
- Context Info: relatedNode holds the parent node
try to dispatch a custom event when add the row and bind it for update the combobox
to bind event use:
$('#something').bind('row.added', function(event) {
event.stopPropagation();
//your code here
}
to trigger the event (after row is added) use:
$('#something').trigger('row.added');
As you all mentioned, the problem is caused from chosen, for each drop box item in chosen, it fires the DOM event, therefore I'll have infinite loop.
I found a simple solution by just filtering the unnecessary events:
$("#listenMe").bind("DOMNodeInserted", function(event) {
var targetName = event.target.nodeName.toString() ;
if (targetName == "TR") {
$(".chzn-select").chosen();
//alert('ha cambiato - Event Node Name:' + event.target.nodeName)
}
});
Therefore it only runs when a new row added into a table
Related
As the title reads, is there a way to update the variable (which links to how many divs there is in a parent)?
The code I have works, but I was looking for another way...
The code I have:
function addDiv() {
//adds a div into the parent
}
parent.addEventListener("mouseover", ()=>{
getAllLists = parent.querySelectorAll(".list-name-tile")
getAllLists.forEach(list => {
list.addEventListener("click", ()=>{
//other code
})
});
})
In my code, I used "mouseover" to update getAllLists; I was just wondering if there was another way to do this and achieve a similar effect or is this the way to it best? Please ask me to clarify or if you need additional information.
Thank you!
Retrieving the div elements in the mouseover event listener is ok in my opinion.
However, there is a problem in your code. Each time you hover over the parent element, the click eventlistener is attached to the child elemens over and over again. You can try this by inserting a console.log() in the click event handler. Hover over the parent element a few times and then click on a child element. You will see the output of the console.log a few times.
This is because the clicker event listener is attached everytime you hover over the parent element.
Possible solution
As far I understand your question you would like to add a click eventlistener to all child element (even the ones which are added dynamically). If thats the case you can make usage of Event Delegation.
Bubbling also allows us to take advantage of event delegation — this concept relies on the fact that if you want some code to run when you select any one of a large number of child elements, you can set the event listener on their parent and have events that happen on them bubble up to their parent rather than having to set the event listener on every child individually. Remember, bubbling involves checking the element the event is fired on for an event handler first, then moving up to the element's parent, etc.
A good example is a series of list items — if you want each one to pop up a message when selected, you can set the click event listener on the parent , and events will bubble from the list items to the .
Event bubbling and capture (scroll down to the section "Event delegation")
Via Event Delegation you can do it this way:
HTML
<div id="parent">
<div class="list-name-tile">First div</div>
<div class="list-name-tile">Second div</div>
</div>
JavaScript
const parent = document.getElementById("parent");
parent.addEventListener("click", e => {
if (e.target.classList.contains("list-name-tile")) {
// Do stuff
}
});
function addDiv() {
const div = document.createElement("div");
div.classList.add("list-name-tile");
div.textContent = "Next div";
parent.appendChild(div);
}
addDiv();
In the example I have attached a click event listener to the parent element. So whenever you click on a child element the event is "bubbling" to the next ancestor which is your parent element. There we have the event listener and can handle it. In the example I have filtered it to only do something when the element, which has triggered the event, has a specific class. You can also filter for specific events and so on.
On my page, the user clicks on an element in order to edit it. To facilitate this, I assign the class editable to all such elements.
How should I listen for clicks on all these elements? Currently, I'm doing this:
document.body.addEventListener("click", (event) => {
if (event.target.classList.contains("editable")) {
// do stuff
}
});
The alternative would be to set a listener on every element, like this:
const editables = document.getElementsByClassName("editable");
for (const editable of editables) {
editable.addEventListener("click", editElement);
}
It seems to me that the first way must be better for performance, since it's only one element being listened on, but is it possible to degrade performance by attaching all such events to the body element? Are there any other considerations (e.g. browser implementations of event handling) that I'm neglecting which would suggest doing it the second way?
Short answer: definitely do it the first way. Event delegation is way more performant, but requires extra conditionals in your code, so it's basically a complexity versus performance tradeoff.
Longer Answer: For a small number of elements, adding individual event handlers works fine. However, as you add more and more event handlers, the browser's performance begins to degrade. The reason is that listening for events is memory intensive.
However, in the DOM, events "bubble up" from the most specific target to the most general triggering any event handlers along the way. Here's an example:
<html>
<body>
<div>
<a>
<img>
</a>
</div>
</body>
</html>
If you clicked on the <img> tag, that click event would fire any event handlers in this order:
img
a
div
body
html
document object
Event delegation is the technique of listening to a parent (say <div>) for a bunch of event handlers instead of the specific element you care about (say <img>). The event object will have a target property which points to the specific dom element from which the event originated (in this case <img>).
Your code for event delegation might look something like this:
$(document).ready(function(){
$('<div>').on('click', function(e) {
// check if e.target is an img tag
// do whatever in response to the image being clicked
});
});
For more information checkout Dave Walsh's blog post on Event Delegation or duckduckgo "event delegation".
NOTE ON CODE SAMPLE IN OP: In the first example, target.hasClass('editable') means that the specific thing clicked on must have the class editable for the if block to execute. As one of the commenters pointed out, that's probably not what you want. You might want to try something along these lines instead:
$(document).on('click', function(e) {
if ($(e.target).parents(".editable").length) {
// Do whatever
}
});
Let's break that down a bit:
$(e.target) - anything that on the page that was clicked converted to jQuery
.parents(".editable") - find all the ancestors of the element clicked, then filter to only include ones with the class "editable"
.length - this should be an integer. If 0, this means there are no parents with "editable" class
Another plus point for the first method
I was using the second (alternative) method that you have mentioned I noticed that when the ajax loaded... the newly created elements were not listening the event. I had to redo the for loop after ajax every time.
With the first method which looks like following in my code also works with ajax.
document.addEventListener('click', function (e) {
if (hasClass(e.target, 'classname')) {
// do stuff
}
}, false);
So first one is better
I am building a Windows 8.1 Store application with WinJS. When the user queries some search results show up in a <p class="searchresults">content</p> tag.
I'd like to add an event handler to the .searchresults class. I've done the following:
$('.searchresults').on('click', function() {
console.log("clicked");
});
I've tried even without .on()
$('.searchresults').click(function() {
console.log("clicked");
});
However the event never gets fired. I've set up a breakpoint, so I can see when it fires - but that never happens
I've tried to add an event handler via the WinJS way:
Microsoft.Maps.Events.addHandler(document.getElementsByClassName("searchresults"), 'click', myfunc);
Without success.
Any ideas why this is happening?
I will guess that you are creating the <p class="searchresults">content</p> object AFTER you try to install the event handler (a common problem with dynamic content). That will not work with normal event handling because the DOM object does not exist when you try to add the event handler to it.
If this is the case, then you need to use delegated event handling like this:
$(document.body).on('click', '.searchresults', function() {
console.log("clicked");
});
This will allow you to dynamically create the searchresults content at any time and the event handler will still fire via event delegation (events propagate up to their parents).
You haven't shown the HTML around the search results content, but the most optimal way to do this is to select the closest static parent to the search results (a parent that is not dynamically created and already exists at the time you attach the event handler) and attach the event to that:
$(closest static parent selector).on('click', '.searchresults', function() {
console.log("clicked");
});
I have the following code:
var $reviewButton = $('span.review_button');
$reviewButton
.live('click',
function(){
$('#add_reviews').show();
}
)
Later in the script, I use an AJAX call to load some content and another instance of $('span.review_button') enters the picture. I updated my code above to use '.live' because the click event was not working with the AJAX generated review button.
This code works, as the .live(click //) event works on both the static 'span.review_button' and the AJAX generated 'span.review_button'
I see however that .live is depracated so I have tried to follow the jquery documentations instructions by switching to '.on' but when I switch to the code below, I have the same problem I had before switching to '.live' in which the click function works with the original instance of 'span.review_button' but not on the AJAX generated instance:
var $reviewButton = $('span.review_button');
$reviewButton
.on('click',
function(){
$('#add_reviews').show();
}
)
Suggestions?
The correct syntax for event delegation is:
$("body").on("click", "span.review_button", function() {
$("#add_reviews").show();
});
Here instead of body you may use any static parent element of "span.review_button".
Attention! As discussed in the comments, you should use string value as a second argument of on() method in delegated events approach, but not a jQuery object.
This is because you need to use the delegation version of on().
$("#parentElement").on('click', '.child', function(){});
#parentElement must exist in the DOM at the time you bind the event.
The event will bubble up the DOM tree, and once it reaches #parentElement, it is checked for it's origin, and if it matches .child, executes the function.
So, with this in mind, it's best to bind the event to the closest parent element existing in the DOM at time of binding - for best performance.
Set your first selector (in this case, div.content) as the parent container that contains the clicked buttons as well as any DOM that will come in using AJAX. If you have to change the entire page for some reason, it can even be change to "body", but you want to try and make the selector as efficient as possible, so narrow it down to the closest parent DOM element that won't change.
Secondly, you want to apply the click action to span.review_button, so that is reflected in the code below.
// $('div.content') is the content area to watch for changes
// 'click' is the action applied to any found elements
// 'span.review_button' the element to apply the selected action 'click' to. jQuery is expecting this to be a string.
$('div.content').on('click', 'span.review_button', function(){
$('#add_reviews').show();
});
Assume I get a table element with ID="emTab", how do I call JS to click it?
Thanks.
document.getElementById("emTab").onclick = function() {
// your code goes here
};
See element.onclick
To trigger click event
document.getElementById("emTab").click();
See element.click
The click method is intended to be
used with INPUT elements of type
button, checkbox, radio, reset or
submit. Gecko does not implement the
click method on other elements that
might be expected to respond to
mouse–clicks such as links (A
elements), nor will it necessarily
fire the click event of other
elements.
Non–Gecko DOMs may behave differently.
When a click is used with elements
that support it (e.g. one of the INPUT
types listed above), it also fires the
element's click event which will
bubble up to elements higher up the
document tree (or event chain) and
fire their click events too. However,
bubbling of a click event will not
cause an A element to initiate
navigation as if a real mouse-click
had been received.
Cross browser way
If you can use jQuery then it would be
$("#emTab").trigger("click");
Firing events cross-browser - http://jehiah.cz/archive/firing-javascript-events-properly
its simple using JQuery
$('#emTab').click(functionToCall);
while in JS
document.getElementById('emTab').onclick = function() {};
for details on DOM events:
http://www.howtocreate.co.uk/tutorials/javascript/domevents