Attaching Event Handlers to JQuery-generated DOM Elements - javascript

I'm pulling some data from an external API and then displaying it in a dashboard page. To do this, I'm generating DOM elements once I've processed the data, like so:
for(var key in companies) {
$(document.createElement("span"))
.attr({ id: key })
.appendTo($("#someDiv"))
.click(function() {
alert(key);
});
$("#"+key).html("<b>" + key + "</b>: $"+companies[key]+"<br>");
}
However, when I click on any of the newly generated span elements, I get an alert with the last value in companies. For example, if I had declared:
var companies = {
"Google": 3,
"Apple": 4
};
then clicking on both the Google span and the Apple span would alert 4. My desired behavior is clicking on the Google span to alert 3.

How about this:-
Attach the event handler using event delagtion with on() just once. (See the class added compName). and just using its id.
See Delegated event handler reference here. If somediv already exist in DOM then you can just use $('#someDiv').on('click','.compName',function(){...
$(function(){
$(document).on('click','.compName',function(){
//.....
alert(this.id);
//....
});
....
for(var key in companies) {
$(document.createElement("span"))
.attr({ id: key, 'class':'compName' }).html("<b>" + key + "</b>: $"+companies[key]+"
<br>").html("<b>" + key + "</b>: $"+companies[key]+"<br>").
.appendTo($("#someDiv"));
}
//...
})

You need to capture the key value with a closure, since the loop will have finished by the time the click handler actually executes. Try this:
.click((function() {
return function () {
alert(key);
};
})());
Or, you can just alert its id, since that's what you set it as:
.click(function () {
alert(this.id);
});

That's because the variable key gets changed before you call the function. You need a closure around it to prevent it from modified by the outer code:
for(var key in companies) {
$(document.createElement("span"))
.attr({ id: key })
.appendTo($("#someDiv"))
.click((function(privatekey) {
return function(){
alert(privatekey);
};
})(key));
$("#"+key).html("<b>" + key + "</b>: $"+companies[key]+"<br>");
}

Related

Incorrect selector is called on click event

There is the following JS:
for (var catId in this.data.categories) {
(function (catId) {
$("li#tsCat-" + catId).click(function (event) {
$(this).addClass("tax-active");
event.stopPropagation();
})
}(catId))
for (var floor in this.data.floors) {
(function (floor, catId) {
var selector = "#tsCatFloor_" + catId + "_" + floor;
$(selector).on("click", function (event) {
$(this).addClass("active-filt");
event.stopPropagation();
});
}(floor, catId));
}
}
This code forms two level list.
On clicking links on 1st level, it executes correct click handler $("li#tsCat-" + catId).
But on clicking 2nd level it again executes 1st handler $("li#tsCat-" + catId).
It needs to execute 2nd handler $(selector).
Where is the problem?
Sorry, I didn't provided the full view, but what is good I found a solution what could be useful for everyone:
If You're adding .click() event be sure, that You put the data (against which .click is called) already to DOM. Otherwise .click will never be called.

jQuery click event lost after appenTo

I am using appendTo to move list items between two list, upon a button click. The button resides in each li element. Each li has two buttons, of which only one is visible at a time, depending on the list the li currently resides.
Here is the function:
// 'this' is the first list
// Click Handler for remove and add buttons
$(this.selector + ', ' + settings.target + ' li button').click(function(e) {
var button = $(e.target);
var listItem = button.parent('li');
listItem.children("button").toggleClass("hidden");
if (button.hasClass("assign")) {
// Add Element to assignment list
listItem.appendTo(settings.target);
}
else
if (button.hasClass("remove")) {
// Remove Element from assignment list
listItem.appendTo(source);
}
})
As long as the list item reside in the original li, the click events in the buttons are triggered. However, once it is moved to the other list using listItem.apendTo. The click item no longer fires. Why is this the case? I cant find anything about this in the docs.
Sometimes jQuery won't be able to find something if it isn't present in the DOM when your script first loads. If it is a dynamically created element, try replacing your click event handlers with 'on'
Rather than:
$(".aClass").click(function(){
// Code here
})
Try:
$("body").on("click", ".aClass", function(){
Code here
})
http://api.jquery.com/on/
You should use on event.
$(".aClass").on("click", function(){
//Your custom code
})
on event is usful for Dynamically generated data + static data already in HTML.
As recommended by user 'apsdehal', a deleate was what i needed:
// Click Handler for remove and add buttons
$(source.selector + ', ' + settings.target ).delegate("li button", "click", function(e) {
var button = $(e.target);
var listItem = button.parent('li');
listItem.children("button").toggleClass("hidden");
if (button.hasClass("assign")) {
// Add Element to assignment list
listItem.appendTo(settings.target);
}
else
if (button.hasClass("remove")) {
// Remove Element from assignment list
listItem.appendTo(source);
}
});

Why is click event handler for a submit button in a colorbox defined in a jQuery prototype method not invoked

I have added a function to jQuery prototype as below. What I want to do is when this method is invoked, generate an html form based on the arguments passed to the method and show it in a colorbox.
(function($) {
$.fn.myFunction = function(data){
var form = $('<form name="people"></form>');
var index;
for (index = 0; index < data.length; index++) {
var match = data[index];
$('<input type="radio" name="person">' + match['name'] + ' [' + match['uri'] + ']<br> ')
.attr("value", match['uri'])
.appendTo(form);
}
$('<input type="button" id="a_button" value="Add"/><br>')
.appendTo(form);
var list = $('<div>').append(form).html();
$('#a_button').click(
function(){
console.log('message from event handler');
}
);
$.colorbox({ innerWidth:420, innerHeight:315, html: list });
};
})(jQuery);
As you can see, form has a button called Add using which I hope to make an ajax request. But unfortunately click event handler attached to this button doesn't seem to be invoked.
Does anyone have any idea about what's wrong here? myFunction is actually invoked by a drupal ajax command in case if that's helpful.
You are appending the form to the DOM after attaching the event handler.
$('#a_button') searches the DOM at that specific point in time, but the form is not added to the DOM until after your call to colorbox with list as a parameter.
Try a permanent delegated event handler instead (or simply add the click handler after the colorbox line).
e.g.
$(document).on("click", "#a_button", function(){
console.log('message from event handler');
});
or
$.colorbox({ innerWidth:420, innerHeight:315, html: list });
$('#a_button').click(
function(){
console.log('message from event handler');
}
);

Jquery creating checkboxs dynamically, and finding checked boxes

I have information that comes out of a database and gets put into a list with a checkbox by each element. This is how it is currently done:
function subjects(){
$.ajax({
url: "lib/search/search.subject.php",
async: "false",
success: function(response){
alert(response);
var responseArray = response.split(',');
for(var x=0;x<responseArray.length;x++){
$("#subjects").append("<br />");
$("#subjects").append(responseArray[x]);
$("#subjects").append("<input type='checkbox' />");
}
}
});
}
it works fine, but I need a way to pick up on if a checkbox is clicked, and if it is clicked then display which one was clicked, or if multiple ones are clicked.
I can't seem to find a way to pick up on the checkboxs at all.
the response variable is "math,science,technology,engineering"
Because you are populating the Checkboxes Dynamically you need to Delegate the event
$("#subjects").on("click", "input[type='checkbox']", function() {
if( $(this).is(":checked") ) {
alert('Checkbox checked')
}
});
To better capture the data it is better if you encase the corresponding data into a span , so that it can be easier to search..
$("#subjects").append('<span>'+responseArray[x] + '</span>');
$("#subjects").on("click", "input[type='checkbox']", function() {
var $this = $(this);
if( $this.is(":checked") ) {
var data = $this.prev('span').html();
alert('Current checkbox is : '+ data )
}
});
It would be best to give your dynamically injected checkboxes a class to target them better, but based on your code try:
$("#subjects").on("click", "input", function() {
if( $(this).is(":checked") ) {
// do something
}
});
Since your input elements are added dynamically, you need to use jQuery's .on() function to bind the click event to them. In your case you need to use .on() to bind to an element that exist in the DOM when the script is loaded. In your case, the element with the ID #subjects.
This note from the docs is mainly for machineghost who downvoted my answer for no apparent reason:
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page.
$('#subjects input[type=checkbox]').on('click',function(){
alert($(this).prop('checked'));
});
or the change event: in case someone uses a keyboard
$('#subjects input[type=checkbox]').on('change',function(){
alert($(this).prop('checked'));
});
simple fiddle example:http://jsfiddle.net/Dr8k8/
to get the array example use the index of the inputs
alert($(this).prop('checked') +'is'+ $(this).parent().find('input[type=checkbox]').index(this)+ responseArray[$(this).parent().find('input[type=checkbox]').index(this) ]);
simplified example: http://jsfiddle.net/Dr8k8/1/
EDIT: Just for an example, you could put the results in an array of all checked boxes and do somthing with that:
$('#subjects>input[type=checkbox]').on('change', function() {
var checklist = [];
$(this).parent().find('input[type=checkbox]').each(function() {
$(this).css('background-color', "lime");
var myindex = $(this).parent().find('input[type=checkbox]').index(this);
if ($(this).prop('checked') == true) {
checklist[myindex] = responseArray[myindex];
}
});
$('#currentlyChecked').text(checklist);
});
EDIT2:
I thought about this a bit and you can improve it by using .data() and query that or store it based on an event (my button called out by its id of "whatschecked")
var responseArray = ['math', 'science', 'technology', 'engineering'];// just for an example
var myList = '#subjects>input[type=checkbox]';//to reuse
for (var x = 0; x < responseArray.length; x++) {
// here we insert it all so we do not hit the DOM so many times
var iam = "<br />" + responseArray[x] + "<input type='checkbox' />";
$("#subjects").append(iam);
$(myList).last().data('subject', responseArray[x]);// add the data
}
var checklist = [];// holds most recent list set by change event
$(myList).on('change', function() {
checklist = [];
$(myList).each(function() {
var myindex = $(this).parent().find('input[type=checkbox]').index(this);
if ($(this).prop('checked') == true) {
checklist.push($(this).data('subject'));
alert('This one is checked:' + $(this).data('subject'));
}
});
});
// query the list we stored, but could query the checked list data() as well, see the .each() in the event handler for that example
$("#whatschecked").click(function() {
var numberChecked = checklist.length;
var x = 0;
for (x = 0; x < numberChecked; x++) {
alert("Number " + x + " is " + checklist[x] + " of " + numberChecked);
}
});
live example of last one: http://jsfiddle.net/Dr8k8/5/
The general pattern to do something when a checkbox input is clicked is:
$('input[type=checkbox]').click(function() {
// Do something
})
The general pattern to check whether a checkbox input is checked or not is:
var isItChecked = $('input[type=checkbox]').is(':checked');
In your particular case you'd probably want to do something like:
$('#subjects input[type=checkbox]').click(function() {
to limit the checkboxes involved to the ones inside your #subjects element.

Javascript variable scope issue with jquery click event

I am trying to assign a series of objects stored in an array to jquery click event handlers.
The problem is , when the event fires, I only ever references the last object in the array.
I have put together a simple example to show the problem:
function dothis() {
this.btns = new Array('#button1', '#button2');
}
// Add click handler to each button in array:
dothis.prototype.ClickEvents = function () {
//get each item in array:
for (var i in this.btns) {
var btn = this.btns[i];
console.debug('Adding click handler to button: ' + btn);
$(btn).click(function () {
alert('You clicked : ' + btn);
return false;
});
}
}
var doit = new dothis();
doit.ClickEvents();
The HTML form contains a couple of buttons:
<input type="submit" name="button1" value="Button1" id="button1" />
<input type="submit" name="button2" value="Button2" id="button2" />
When button1 is clicked, it says "You clicked #Button2"
It seems that both button click handlers are pointing to the same object inside var btn.
Considering the variable is inside the for loop, I cannot understand why.
Any ideas?
You need a function factory to close the loop variable, such as this:
//get each item in array:
for (var i=0; i<this.btns.length; i++) {
$(this.btns[i]).click(function(item) {
return function () {
alert('You clicked : ' + item);
return false;
}
}(this.btns[i]));
}
Another good option is to let jquery help you. Use jQuery.each(). The variable btn here is local to the handler function, and so isn't reused between iterations. This allows you to close it and have it keep its value.
$.each(this.btns, function() {
var btn = this;
$(this).click(function () {
alert('You clicked : ' + btn);
return false;
}
});
within an event handler, 'this' usually refers to the element firing the event, in this case, it would be your button
so the solution to your problem is fairly easy, instead of referencing the btn variable, which lives in a higher scope and gets mutated long before the event handler fires, we simply reference the element that fired the event and grab its ID
$(btn).click(function () {
alert('You clicked : #' + this.id);
return false;
});
Note: if your array contains other selectors that just the ID, this will obviously not reflect that and simply continue to show the ID
Lucky, the click handler (and all other event handlers afaik) take an extra parameter for eventData, useful like so:
$(btn).click(btn, function (event) {
alert('You clicked : #' + event.data);
return false;
});
User an array if you're passing multiple things:
$(btn).click(['foo', 'bar'], function (event) {
alert('this should be "foo": ' + event.data[0]);
alert('this should be "bar": ' + event.data[1]);
return false;
});
you have to use closures for this.
i'm not sure if i remember the correct syntax but you could try this:
$(btn).click(function () {
return function() {
alert('You clicked : ' + btn);
return false;
}
});
maybe you need to change just the click binding:
$(btn).click(function () {
alert('You clicked : ' + $(this).attr('id'));
return false;
});
Your problem is here:
alert('You clicked : ' + btn);
btn retains the value from the last time it was called in the loop. Read the value from the button in the event.
$(btn).data('selector', btn).click(function () {
alert('You clicked : ' + $(this).data('selector'));
return false;
});
http://jsfiddle.net/Mc9Jr/1/

Categories

Resources