I have a problem. I've a function dropcopy() that calls on a div for drag&drop.
function dropcopy(ev) {
ev.preventDefault();
elencoOggetti = new Array();
var copyimg = document.getElementById(data).cloneNode(true);
//copyimg.id = data;
ev.target.appendChild(copyimg);
}
This function copy the object but not work the listener on clone. The listener is this
$("#filtro").click(function() {
alert('ciao');
});
Why isn't it working? Please help me.
First of all, you shouldn't clone an element without changing its id. IDs must be unique.
Event listeners are binded to the first element when created. Clones doesn't have the listeners binded. You can bind it again, or bind the listeners to a parent object instead of the objects themselves. You can do that like this:
$('body').on('click', '#filtro', function(){
alert('ciao');
});
This means the listener is attached to the body instead of the element itself, and it'll be fired only if the event was fired by #filtro.
I belive that you are binding the element, when it is not existing, so add the guard:
$(document).ready(function(){
$("#filtro").click(function(){
alert('ciao');
});
}
If you wanna bind it after cloning, you can rerun this function above:
function resetUI(){
$("#filtro").click(function(){
alert('ciao');
});
}
Or overwrite the DOM, look on the docs of jquery if you wanna bind that to every name on update.
Related
I have a class method which defines the event listeners. Let us use the following code snippet for simplicity.
function bindEvents() {
document.querySelector('button').addEventListener('click', (e) => {
console.log('clicked!');
});
}
// Initial event binding
bindEvents();
// Rebind events at some point for dynamically created elements
bindEvents();
<button type="button">Click</button>
Everything works fine when using bindEvents() only once, however for example calling it again in ajax callback results in listener executed twice. So this means after second bindEvents(), clicking the button will console.log() twice and so on. Is there a way I can get around this behavior?
I know I can bind the events "dynamically" on the document and check with e.target, but there is a situation where I need mouseenter/mouseleave events and I don't think it's a good idea to always have those eventListeners on the document.
I've read somewhere the following, but it seems false...
The .addEventListener method ensures that the same function reference
won't be bound more than once to the same element/event/captures
combination.
Also I have played with the options parameter from here https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener, but without success.
Excuse me if this was answered somewhere, but I failed to find answer in SO and the search engines.
UPDATE: Is there a way to overwrite existing eventListeners or old ones should be removed with removeEventListener like kcp suggested below? Is there more elegant solution to this problem at all?
The .addEventListener method ensures that the same function reference won't be bound more than once to the same element/event/captures combination.
In your case, each time you execute bindEvents() a completely new handler is passed to the click event listener since you define new function (no matter it looks the same, it is different object). To use the same handler each time you must define it outside bindEvents and pass it by name (by reference). This works as expexted:
function clickHandler(e){
alert('clicked!');
}
function bindEvents() {
document.querySelector('button').addEventListener('click', clickHandler);
}
// Initial event binding
bindEvents();
// Rebind events at some point for dynamically created elements
bindEvents();
<button>click</button>
However with jQuery I use the following approach which allows me to specify that only elements in a specific container (context or ctx) will be bound:
$.fn.bindEvents = function bindEvents(ctx){
ctx = ctx || this;
$('button', ctx).on('click', function(event){
alert(1);
});
};
$('body').bindEvents();
$('div').bindEvents();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>
<div><button>in div </button></div>
In the example above bindEvents is executed twice but the first button is bound only once since it's not in a div. While if you click on the second button it alerts twice because satisfies both contexts.
addEventListener does not overwrite existing event listeners, it simply adds a new one as the method name implies. Existing listeners must be removed using the removeEventListener method.
function onClick($event) {
console.log('clicked!');
}
function bindEvents() {
/** Remove event listener first **/
document.querySelector('button').removeEventListener('click', onClick);
document.querySelector('button').addEventListener('click', onClick);
}
removeEventListener docs
Apart from removeEventListener, You can also use Event delegation. Using this mechanism event is handler by attaching event listener to parent element.
var elem = document.querySelector('div');
elem.addEventListener('click', function(e) {
e = e || event
var target = e.target;
if (target.nodeName != 'BUTTON')
return;
console.log('clicked ' + target.textContent);
});
//Simulate addition of dynamic elements
setTimeout(function() {
var newButton = document.createElement('button');
newButton.innerHTML = 'Click 2';
elem.appendChild(newButton)
}, 2000)
<div>
<button type="button">Click</button>
</div>
I've looked all over the internet with everyone giving the same answer
$(document).on('click', '#targetID', function() {
// do stuff
});
instead of
$('#targetID').click(function() {
// do stuff
});
This is nice and it works fine, if you have a click event. But within that on click function, the part where it says do stuff, how can I now target an appended element? For instance say I append 2 divs back to back.
<div id="mainDiv"></div>
<script>
socket.on('event', function (data) {
$('#mainDiv').append ('<div class="1st" id="'+data.id+'">one</div>
<div class="2nd" id="'+data.id+'">second</div>');
});
$(document).on('click', '.1st', function() {
//and right here i would like to`enter something like
$('.2nd').css('background-color','yellow');
}
</scirpt>
This however seems not to work because to my knowledge, this element hasn't been added to the DOM. So what should I do? Should i use angular.js for this?
PS I've also tried adding the entire appended content into a variable first before appending that variable. and then using variable.find to find the element within to no avail. The variable only has context within that function, but is null in the on click function. Thanks in advance for any information that broadens my understanding of this.
The delegation of 'on' is correct. Once the div element exists in the dom, clicking should work. My only concern is you have named your classname beginning with a number. Maybe name it with an alpha character followed by a number.
The difference between the 2 is the concept of event binding vs event delegation.
$('#targetID').click(function() { is event binding which works on elements as long as they exist in the markup when the page or document loads.
$(document).on('click', '#targetID', function() { is event delegation which means the event would listen to the document for the click event on the element with ID targetID if it exists in the DOM when the page loads or if it is dynamically added.
So In your case, its event delegation since you are dynamically adding the elements. But in order to make it work, you need to register the listener on the document ready event for the document to listen to the event on the element #targetID
<script>
$(document).ready(function() // Add this
{
socket.on('event', function (data) {
$('#mainDiv').append ('<div class="1st" id="'+data.id+'">one</div><div class="2nd" id="'+data.id+'">second</div>');
});
$(document).on('click', '.1st', function() {
//and right here i would like to`enter something like
$('.2nd').css('background-color','yellow');
});
});
</script>
Here's an example : https://jsfiddle.net/nobcp0L7/1/
I want to add mouse events on to the class .piece even if the divs are created after the DOM is loaded.
Here is my actual loop:
var piece = document.getElementsByClassName('piece');
function theLoop() {
for(var i=0; i<piece.length; i++) {
piece[i].addEventListener("mousemove", function(event) {
//do stuff
}
}
}
theLoop();
It works fine, but if I add new divs with the class .piece after the DOM loaded, the loop ignores them.
I add those new divs with .cloneNode() and .appendChild(), like this :
function createPiece(symbol, name) {
var clone = document.getElementById(symbol).cloneNode(true);
clone.setAttribute('id',symbol+'-'+name);
document.getElementById(name).appendChild(clone);
}
createPiece(symbol, name);
All the original cloned divs have the class .piece.
How can I add those new divs to my for loop, and have the mouse event attached to them?
Thanks.
If your loop runs on page load, it's not possible to make it account for elements added to the DOM dynamically afterwards. Usually, event delegation would be a solution, but it's not a good solution for the mousemove event (since it fires so often).
What you could do is create a function that adds the event handler to a passed element. You could then call that function from your current loop, and call it again after adding each new element to the DOM.
Put a mousemove event handler on a common parent of all the pieces and then examine e.target in the event data structure to see which piece the event actually occurred on.
This is called delegated event handling and allows you to install just one event handler on a static parent that will give you events from all children, even children that are dynamically added later.
The only other option is to install an event handler on each specific piece when it's later added to the page. You would need to trigger this from the actual code that adds the element to the page as there is no well-supported, cross-browser way to watch for DOM modifications. If you do it this way, you will need to make your event handling function a named function (rather than the anonymous function you are using now) so that you can use the same function in multiple places.
You don't need the loop for newly created elements. Nor do you need event delegation (though it can be useful).
Just make the handler a named function, and bind when you clone.
var piece = document.getElementsByClassName('piece');
function pieceHandler(event) {
//do stuff
}
function theLoop() {
for(var i=0; i<piece.length; i++) {
piece[i].addEventListener("mousemove", pieceHandler, false)
}
}
theLoop();
function createPiece(symbol, name) {
var clone = document.getElementById(symbol).cloneNode(true);
clone.addEventListener("mousemove", pieceHandler, false); // assign handler
clone.setAttribute('id',symbol+'-'+name);
document.getElementById(name).appendChild(clone);
}
createPiece(symbol, name);
I've got a bunch divs which each contain a remove link attached with the click event below:
var observeRemoveRoom = function
$('.remove_room').click(function(){
$(this).parent().removeClass('active');
});
}
Clicking it removes the 'active' class of the parent (the div). I call this observeRemoveRoom function on window load which works fine.
The thing is, I have another function which adds more of the same divs. Since the a.remove_room links contained within the new divs weren't around on window.load I need to call observeRemoveRoom.
Am I somehow duplicating the event handlers? Does jQuery overwrite them? If so should I unbind the handlers?
Each time you call observeRemoveRoom jQuery will add a new unique event handler function for a click event.
So yes, you need to .unbind() either all currently bound handlers by just calling .unbind() without arguments, or be specific and pass in a function reference.
You can try a live query to keep them updated: http://docs.jquery.com/Plugins/livequery
Yes, you will be duplicating the event-handlers if you call observeRemoveRoom again, but it might not be noticeable since you are only calling the removeClass method which does nothing if the class is not found, which would be the case after the first listener is triggered.
Instead you can un-bind and re-bind the click event each time, like:
var observeRemoveRoom = function(){
var remove_class = function(){
$(this).parent().removeClass('active');
};
$('.remove_room').off('click', remove_class).on('click', remove_class);
}
But that said, it is recommended that you do this outside this function`, rather than binding and unbinding the event every time, like:
$(document).ready(function(){
var remove_class = function(){
$(this).parent().removeClass('active');
};
// If the element exists at dom ready, you can bind the event directly
$('.remove_room').on("click", remove_class);
// If the element is added in dynamically, you can [delegate][1] the event
$('body').on("click", '.remove_room', remove_class);
// Note: Although I've delegated the event to the body tag in this case
// I recommend that you use the closest available parent instead
});
http://api.jquery.com/on/#direct-and-delegated-events : [1]
Im trying to remove a row. But i can't figure it out really.
First of all I've got a button that'll add a new row, that works.
But I want to give the user the possibility to remove the just added row.
The row has an 'div' inside what acts as an deletebutton. But the deletebutton doesn't work?
I'm I missing something?
Greet,
Juki
function addrow() {
$("#container").append("<div id=\"row_added\"><div class=\"deletebutton\" id=\"delbuttonnumber\"></div><div id=\"addedcolor\" class=\"colorcube\"></div></div>");
}
// deleterow the row
$(".deletebutton").click(function() {
rowclrid = "#" + $(this).parent().attr("id");
$(rowclrid).remove();
});
My guess is that you have assigned the click handler before adding the new content, so the handler is not attached to this particular element. You can use .delegate() to listen for events on all elements below a particular parent element, whether or not they already exist:
$('#container').delegate('.deletebutton', 'click', function(){
$(this).parent().remove();
});
This listens for all click events that happen inside the element #container using a feature of the DOM called event bubbling, then checks to see if they happened on a .deletebutton element, and, if so, calls the handler.
Note also the simplified code inside the handler.
I think what you need to do is use the live method. This will add the events for any new item that is added to the DOM, not just the ones that exist when you set the click handler.
// deleterow the row
$(".deletebutton").live('click', function() {
$(this).parent().remove();
});
You also don't need to get the row by getting the ID - it's simpler in the way above.