How to corectly bind events in a loop - javascript

On a page I have couple of divs, that look like this:
<div class="product-input">
<input type="hidden" class="hidden-input">
<input type="text">
<button class="remove">X</button>
</div>
I'm trying to bind an event to that remove button with this code (simplified):
$('.product-input').each(function() {
product = $(this);
product_field = product.find('.hidden-input');
product.on('click', '.remove', function(event) {
product_field.val(null);
});
});
It works perfectly when there is only one "product-input" div. When there is more of them, all remove buttons remove value from the hidden field from the last product-input div.
https://jsfiddle.net/ryzr40yh/
Can somebody help me finding the bug?

You dont need to iterate over the element for binding the same event. you can rather bind the event to all at once:
$('.product-input').on('click', '.remove', function(event) {
$(this).prevAll('.hidden-input').val("");
});
If the remove buttons are not added dynamically, you will not need event delegation:
$('.remove').click(function(event) {
$(this).prevAll('.hidden-input').val("");
});
Working Demo

You need to declare product and product_field as local variables, now they are global variables. So whichever button is clicked inside the click handler product_field will refer to the last input element.
$('.product-input').each(function() {
var product = $(this);
var product_field = product.find('.hidden-input');
product.on('click', '.remove', function(event) {
product_field.val(null);
});
});
Demo: Fiddle
But you can simplify it without using a loop as below using the siblings relationship between the clicked button and the input field
$('.product-input .remove').click(function () {
$(this).siblings('.hidden-input').val('')
})
Demo: Fiddle

Related

Event not firing on input change for appended elements

In my form, there's a button to add additional elements as needed by the user. For each input field, there's a .change() handler. The issue is that the .change does not fire on appended form elements but still triggers on existing form elements. Any help is appreciated.
$('.element').each(function() {
$mainElement = $(this); // memorize $(this)
$sibling = $mainElement.next('input'); // find a sibling to $this.
$sibling.change(function($mainElement) {
return function() {
$mainElement.text('My textfield changed');
}
}($mainElement));
Jsfiddle demo: http://jsfiddle.net/WCDBr/22
Try the on method:
$(document).on('change', 'input', function() {});
//or
$(document).on('change', '.inputclass', function() {});
It registers the event with the document and it should work even for newly added documents. Your change event registered with existing elements.
Just for the sake of simplicity i copy pasted the code.
On Clicking the "Add" button you not binding the change event again for the newly created element. Do This on Click:
$('#test').click(function(){
$('ul').append('<li><p class="element">Lorem ipsum</p><input/></li>');
$('.element').each(function() {
$mainElement = $(this); // memorize $(this)
$sibling = $mainElement.next('input'); // find a sibling to $this.
$sibling.change(function($mainElement) {
return function() {
$mainElement.text('My textfield changed');
}
}($mainElement));
});
});

Get data-val on a programatically added <li>

I'm trying to get an programatically added li data-val on click, as below:
$(function () {
$("#bpadd").click(function () {
var bpinput = document.getElementById("bpinput").value;
$('#bpitem').append('<li data-val="' + bpinput + ' clicked">' + bpinput + '</li>');
});
$('#bpitem li').click(function () {
alert($(this).attr('data-val'));
});
This is the html sample:
<input type="text" id="bpinput">
<input type="button" id="bpadd" value="+">
<ul id="bpitem">
<li data-val="Test 1 clicked">Test 1</li>
</ul>
If I click in Test 1, it works, but nothing happens when I click in a new item, why is that so?
You need to use the .ondoc function on some selector that'll work for every newly added element, like this:
$('#bpitem').on('click', 'li', function () {
alert($(this).attr('data-val'));
});
As you can see on the docs, the function will bind the events on newly-added elements that match the selector rather than just binding it to existing ones like the .click function does.
Try this:
$('#bpitem').on('click', 'li', function () {
alert($(this).attr('data-val'));
});
The .click() method binds the click handler only to elements that exist at the time that line of code runs. If you use .on(), you bind the handler to your parent #bpitem element (which exists initially) but when a click occurs jQuery checks if the clicked child element matches the selector in the second parameter and thus it works on children of #bpitem that were added dynamically.
try:
$('#bpitem').on('click', 'li', function() {
alert('clicked');
);
This will dynamically add event listeners to ALL bpitem li elements instead of the ones that are there when this is first ran

how to judge which button has been clicked using there className instead of ID

I have 100 buttons in a table having same class name but different id c-1,c-2,....,c-n <input type="button" class="btn-c" id="c-1" value="ADD">
how will i Know which button has been clicked using their className and whithout using onclick event on the each button
<input type="button" ... onclick="call_function(this);"
for simplicity let say I want to alert(button.id); on the click of any of the 100 buttons
If you have so many buttons, it makes sense to use event delegation:
$('table').on('click', '.btn-c', function() {
alert(this.id); // will get you clicked button id
});
This is optimal approach for performance standpoint as you bind only one event handler to parent element and benefit from child element event bubbling.
UPD. This is pure javascript version of the same code:
document.getElementById('table').addEventListener('click', function(e) {
if (/\bbtn-c\b/.test(e.target.className)) {
alert(e.target.id);
}
}, false);
Demo: http://jsfiddle.net/zn0os4n8/
Using jQuery - attach a click handler to the common class and use the instance of this to get the id of the clicked button
$(".btn-c").click(function() {
alert(this.id); //id of the clicked button
});
You need to attach an event to a parent element and listen for the clicks. You can than use the event object to determine what is being clicked on. You can check if it is the element you want and do whatever you want.
document.body.addEventListener("click", function (e) { //attach to element that is a parent of the buttons
var clickedElem = e.target; //find the element that is clicked on
var isC = (clickedElem.classList.contains("c")); //see if it has the class you are looking for
var outStr = isC ? "Yes" : "No"; //just outputting something to the screen
document.getElementById("out").textContent = outStr + " : " + clickedElem.id;
});
<button class="d" id="b0">x</button>
<button class="c" id="b1">y</button>
<button class="c" id="b2">y</button>
<button class="c" id="b3">y</button>
<button class="d" id="b4">x</button>
<div id="out"></div>
Note: this is not going to work in older IEs without polyfills.

JQuery focus to previous clicked input bug

I have two input fields. The main idea is that whenever you focus to one of the fields, the button click should print a random number inside it.
My problem is that when you just focus on (click on) the first field, then focus on second (or vice versa), the button click prints to both instead of just to the (last) focused field.
You can try to recreate the problem here: http://jsfiddle.net/sQd8t/3/
JS:
$('.family').focus(function(){
var iD = $(this).attr('id');
$("#sets").one('click',function() {
var ra = Math.floor((Math.random()*10)+1);
$('#'+iD).val(ra);
});
});
HTML:
<center class='mid'>
<input type="text" class="family" id="father" />
<br/><br>
<input type="text" class="family" id="mother" />
<br/>
<br/>
<input type='submit' value='Set text' id="sets"/>
</center>
In the "focus" handler, unbind any existing "click" handler:
$('#sets').unbind('click').one('click', function() { ... });
The way you had it, an additional one-shot "click" handler is bound each time a field gets focus, because jQuery lets you bind as many handlers as you like to an event. In other words, calling .one() does not unbind other handlers. When the click actually happens, all handlers are run.
edit — sorry - "unbind" is the old API; now .off() is preferred.
Put the variable iD outside, and separate the functions:
http://jsfiddle.net/sQd8t/8/
This prevents from adding too many events on each input click/focus.
No need to unbind.
var iD;
$('.family').focus(function() {
iD = $(this).attr('id');
});
$("#sets").on('click', function() {
var ra = Math.floor((Math.random() * 10) + 1);
if (iD!="")$('#' + iD).val(ra);
iD = "";
});
See http://jsfiddle.net/sQd8t/11/
$('.family').focus(function(){
$("#sets").attr('data-target',$(this).attr('id'))
});
$("#sets").click(function() {
var target=$(this).attr('data-target');
if(target){
var ra = Math.floor((Math.random()*10)+1);
$('#'+target).val(ra);
}
});
You can create a data-target attribute which contains the field which must be modified.

Event handler triggered twice instead of once

I am sorry that I have asked two questions in a few minutes.
In a html file, I got three child DIV tags in a parent DIV tag:
<div id="container">
<div id="frag-123">123</div>
<div id="frag-124">124</div>
<div id="frag-125">125</div>
</div>
Now when I click either the three child DIV tags, I will see two alert boxes pop up instead of one:
The first alert box will show something like this:
frag-123, and the second alert box will show something like this:
container
I dont know why.
I just want to get the ID value of a child DIV, not the one from the parent DIV.
<script>
$(function() {
$("div").click(function() {
var imgID = this.id;
alert(imgID);
});
});
</script>
Please help.
This is a case of event bubbling. You can stop event bubbling by giving
e.stopPropagation()
inside the click event handler.
Try
$(function() {
$("div").click(function(e) {
var imgID = this.id;
alert(imgID);
e.stopPropagation() // will prevent event bubbling
});
});
If you want to bind click event to only child elemets inside the container div then you can give like this
$("#container div").click(function(){
var imgID = this.id;
alert(imgID);
});
That's because you're binding the event handler to all DIVs. Instead, what you want is bind it only to DIVs within container:
<script type="text/javascript">
$(function() {
$("#container div").click(function() {
var imgID = this.id;
alert(imgID);
});
});
</script>

Categories