Binding events to list elements - javascript

I'm dynamically creating a list using JQuery and would like to bind each element to a "tap" event. How would I do this? Here is a sample of what I'm trying to do, but it isn't working.
for(var i=1; i <= 5; i++) {
var id = "item" + i;
var li = $("<li data-theme=\"d\" id=\"" + id + "\">Item " + i + "</li>");
li.appendTo(ul);
$(document).delegate("#"+id, "tap", function() {
$("#"+id).attr({ "data-theme" : "e", "class" : "ui-li ui-li-static ui-btn-up-e" });
});
}
This code triggers when tapping any element, however it always modifies the last element in the list for some reason.

YOUR OPTIONS
Either move the event handling to outside the loop
for(var i=1; i <= 5; i++) {
....
}
$(document).delegate("[id^=item]", "tap", function() {
});
Use the bind method and apply the tap event to the element, and not to the document.
for(var i=1; i <= 5; i++) {
//append li to ul
$("#"+id).bind("tap", function() {
$(this).attr({
"data-theme" : "e",
"class" : "ui-li ui-li-static ui-btn-up-e"
});
});
}
BUT, the best way to be to put the event outside the loop, and bind the event to ul which will later delegate it to li.
for(var i=1; i <= 5; i++) {
....
}
$("ul").delegate("[id^=item]", "tap", function() {
});
NOTE
If you want to change your theme, you also need to update your layout once.
$("ul").delegate("[id^=item]", "tap", function() {
$(this).attr({
"data-theme" : "e",
"class" : "ui-li ui-li-static ui-btn-up-e"
}).parent("ul").listview().listview("refresh");
});
USE OF STARTS WITH SELECTOR
You've put this in your code :
var id = "item" + i;
That means for the whole loop of 5 elements, your ids are gonna look like this :
<li id="item1">..
<li id="item2">..
<li id="item3">..
<li id="item4">..
Looking at the common thing here, I'd say it is :
item
So, since your ids start with item you could generalise it by using the starts with selector. So,
id^=item
means that you're searching for elements with id that starts with item. And since its an attribute,
[id^=item]
A MORE MODULAR APPROACH
This method involves lesser HTML :
//your ul tag
var ul = $("ul")
//set up an array for adding li to it.
var li = [];
//a temporary element to store "li"
var $li;
for(var i=1; i <= 5; i++) {
//add required attributes to the element
var $li = $("<li/>", {
"data-theme" : "d",
"id" : "item" + i,
"html" : "Item " + i
});
//push that into array
li.push($li);
}
//wait for append to finish
ul.append(li).promise().done(function () {
//wait for list to be added - thats why you wait for done() event in promise()
//add the click events to this - event delegation - this way your click event is added only once
$(this).on("tap", "[id^=item]", function () {
$(this).attr({
"data-theme" : "e",
"class" : "ui-li ui-li-static ui-btn-up-e"
}).parent("ul").listview().listview("refresh");
});
//then refresh
$(this).listview().listview("refresh");
});
Here's a demo for this : http://jsfiddle.net/hungerpain/TdHXL/
Hope this helps!

Just move the event handler to outside the for loop.
And replace
$(document).delegate("#"+id, "tap", function() {
with
$(document).delegate("[id*=item], "tap", function() {
JS
for (var i = 1; i <= 5; i++) {
var id = "item" + i;
var li = $("<li data-theme=\"d\" id=\"" + id + "\">Item " + i + "</li>");
li.appendTo(ul);
}
$(document).delegate("[id*=item]", "tap", function () {
$("#" + id).attr({
"data-theme": "e",
"class": "ui-li ui-li-static ui-btn-up-e"
});
});

Here is what I'd do:
var items = []; // an actual JavaScript array - a list of items.
for(var i=1;i<=5;i++){
items.push({theme:'d'}); //add an item to our list,
//Anything that actually relates to the item should be
//here, text, other data and such. This is our 'view model'.
}
items.forEach(function(item,i){ // for each item, bind to the dom
var el = document.createElement("li"); //create an element
el.textContent = i+1; // set its text
el.onclick = function(e){ // or ontap or whatever
el.className = "ui-li ui-li-static ui-btn-up-e";
item.theme = "d";
}//you mind want to use addEventListener instead at a later point
item.el = el;
ul.appendChild(el); // you need access to ul, currently being a jQuery object ul[0].
});
Note we have access to the items directly from our code, we can update them directly and such and have a direct reference to them. We don't need to query our own data - we own it and know directly how to get to it.
Also - we don't have a 80kb dependency. No complex selectors, no 'magic' bindings. Everything is straight forward and it's just plain ol' javascript.
Note: forEach should be shimmed (easily) for older browsers.

Related

Javascript Print <li> with data-name

I'm trying to use JavaScript to generate JQuery ListView, once I click on the listview item it will show an alert message.
In the demo I found in Stack Overflow it works perfectly.
Once I try to integrate my code with it, it does not work anymore. Is the way I'm generating data-name in my listview is correct?
Sample of my Fiddle: http://jsfiddle.net/TLGY7/416/
$('#mylistview ul').children('li').bind('click', function(e) {
var item = $(this).attr('data-name')
alert('Selected Name=' + item );
});
function view() {
if (db==null) {
alert('Error: Database is null.');
return;
}
$('#listData').empty();
db.executeSql(
'SELECT `id`, `activityname`, `location`, `sdate`, `stime`, `reporter` FROM `record` ORDER BY `id` ASC',
[],
function(resultset) {
var length = resultset.rows.length;
for(var i=0; i<length; i++) {
$('#listData').append('<li class="activitylist" data-name='+resultset.rows.item(i).id+'>'+resultset.rows.item(i).id+'.'+stringconverter(resultset.rows.item(i).activityname)+'</li>');
}
$("#listData").listview( "refresh" );
},
function(error) {
alert('Error: '+error.message);
}
);
}
var linkElement = $('<a href="#pageDetail" class="anchor">').text(resultset.rows.item(i).id + '.' + stringconverter(resultset.rows.item(i).activityname));
var listElement = $('<li class="activitylist">').attr('data-name', resultset.rows.item(i).id)).append(linkElement);
$('#listData').append(listElement)
append method requires JQuery node element, not text
you also can make a $() around your '' statement. the node building itself looks ok for the first view.
If you are adding the listitems dynamically, you should use event delegation to handle the click event:
$('#mylistview ul').on("click", "li", function(){
var item = $(this).data('name')
alert('Selected Name = ' + item );
});
In his way the listitems don't have to exist yet when you setup the click handler.
Updated FIDDLE

How to create "change" event handlers dynamically?

I create dropdown dynamically.
Their ids are ddl1, ddl2, ddl3 and so on.
$('#ddl1').focus(function() {
var previous = this.value;
}).change(function() {
var index = $(this).find('selected').index();
$('#ddl1').find('option:eq(' + index + ')').hide();
$('#ddl2').find('option:eq(' + index + ')').hide();
});
$('#ddl2').focus(function() {
...
Creation of 7 dropdowns makes me to write 7 change event handlers.
How to implement dynamically create change event?
My dropdown append methods are:
var x=1;
var dropdown=('#ddl1').html();
$('#btnadd').click(function() {
$('#divname').append('<select id="ddl'+x+' > </select> <option>../option>');
x++;
});
Give a common class to every dropdown and refer to that. Also, use .on() (event delegation) to catch dynamic created elements:
$(document).on('change', '.common-class', function() {
//Your function
var currentId = $(this).attr('id');
});
UPDATE
Add the same class every time you append a select element:
//...
$('#divname').append('<select id="ddl'+ x + '" class="common-class"><option>../option></select>');
SELECTED OPTION
In order to get the selected option:
$('#current-id').find('option:selected').index();
Variant without delegated events to reduce resource usage:
var $dds = $('select');
$dds.each(function(index) {
var $this = $(this);
processDdChange($this);
});
function processDdChange($obj) {
$obj.on('change', function(e) {
var $this = $(this);
alert($this.attr('id') + ' | ' + $this.find(':selected').val());
});
}
https://jsfiddle.net/tasmanangel/2p9mbs7h/

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.

How to I undo .detach()?

I'm using JQuery 1.5 and the following code to detach li elements w/ a certain class when a button is clicked. What I want to know is, when that button is clicked again, how do I add the element back onto the page?
<script>
$("#remove").click(function () {
$('li.type').fadeOut(300, function() { $(this).detach(); });
});</script>
The question is: where on the page do you want to put the element back? If, for example, all the li elements go back inside <ul id="foo"></ul> you might use something like this:
var items = [];
$('li.type').fadeOut(300, function() {
items.push( $(this).detach() );
});
$('#replace').click(function() {
for(var i = 0; i < items.length; i++) {
$("ul#foo").append(items[i]);
}
items = [];
});
here u can't for loop.
var demo;
$('li.type').fadeOut(300, function() {
demo = $(this).detach();
});
$('#replace').click(function() {
$("ul#foo").append(demo);
});

multiple functions in the document.ready function

here's my code:
$(document).ready(function () {
$('.flip1').click(function () {
$('.panel1').slideToggle("slow");
});
$('.flip2').click(function () {
$('.panel2').slideToggle("slow");
});
$('.flip3').click(function () {
$('.panel3').slideToggle("slow");
});
$('.flip4').click(function () {
$('.panel4').slideToggle("slow");
});
});
I want to make a loop with .flip as the variable (flipVar) and .panel as (panelVar)
Well if it were my page I'd make sure that those elements all shared a class so that I wouldn't need to loop. However, you could do this:
for (var i = 1; i <= 4; ++i) $('.flip' + i).click((function(i) {
return function() { $('.panel' + i).slideToggle('slow'); };
})(i));
The loop variable has to be trapped in a closure so that each "click" handler references the proper value. Again, I really wouldn't do it this way. I'd make the "flip" elements share a class, and then keep that index (the implicit reference to a corresponding "panel") in a separate class element or in a "data-" attribute. Then the handler could find the panel using that value.
edit — as a hack, you could leverage the fact that the class names of the related elements are both of the form "somethingNN", where "NN" is the numeric part. You could strip off the number and then append it to "panel":
for (var i = 1; i <= 4; ++i) $('.flip' + i).click(function() {
var panelClass = this.className.replace(/.*\bflip(\d+).*/, "panel$1");
$(panelClass).slideToggle('slow');
});
Even though you want to run the selector in a loop, I wouldn't do it like that because you're doing multiple DOM selections. You can get it done with one DOM selection:
$(document).ready(function () {
$('div[class^=flip]').each(function ( idx ) {
$(this).click(function() {
$('.panel' + (idx + 1)).slideToggle("slow");
});
});
});
This will work assuming that the flip elements occur on the page in their numerical order.
This uses the attribute starts with selector to get all <div> elements that have a class name starting with "flip". Change the tag name if it is different, or remove it if they don't all have the same tag.
It uses the index that .each() makes available to you in order to toggle the correct .panel.
$(document).ready(function () {
for (var i=1; i<=4; i++) {
(function(i) {
$('.flip'+i).click(function () {
$('.panel'+i).slideToggle("slow");
});
})(i);
}
});
try this:
$(function(){
for(var i=1;i<5;i++){
$('.flip'+i).click(function () {
$('.panel'+i).slideToggle("slow");
});
}
});
PS:- don't use this it will not work as pointed out by #patrick

Categories

Resources