user2066880
user2066880

Reputation: 5024

Binding events to list elements

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.

Upvotes: 0

Views: 333

Answers (3)

krishwader
krishwader

Reputation: 11381

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!

Upvotes: 3

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276596

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.

Upvotes: 1

Sushanth --
Sushanth --

Reputation: 55750

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"
    });
});

Upvotes: 1

Related Questions