phil123
phil123

Reputation: 195

Append <li> element to <ul> and add click event for each?

I'd like to add a series of <li> elements to a <ul>, and add a click event to each one, programmatically.

I'm not sure how to do this, at least not in a neat, jQueryish way.

This is my existing code:

<ul id="saved-list"></ul>
<script type="text/javascript">
$.each(all_objects, function() {{
    var list_route = "<li><a href='#saved-route'>" + this.text + "</a></li>";
    $('#saved-list').append(list_route);      
    // add unique id (this.id) to item and click event here?
    // pseudocode - onclick: alert(this.id);
});
$('#saved-list').refresh('listview'); // jquery mobile list refresh
</script>

Please could someone advise how to add a click event to each list item programmatically?

UPDATE: I need to do something slightly different for each list item (let's just say an alert) - apologies for not making this clear.

Upvotes: 6

Views: 26900

Answers (10)

Mike Axiak
Mike Axiak

Reputation: 11994

@Matt Ball is pretty close to the answer here, but I will add a little more clearly that you can do different things with the delegate depending on what element was clicked:

<ul id="saved-list"></ul>
<script type="text/javascript">
var $savedList = $("#saved-list");
$.each(all_objects, function() {
    $savedList.append("<li><a href='#saved-route'>" + this.text + "</a></li>");
});
$savedList.delegate("li", "click", function (e) {
    alert($(this).text());
});

$('#saved-list').refresh('listview'); // jquery mobile list refresh

Note that in the delegate this is still referring to the li that was clicked on.

Upvotes: 4

Erick Petrucelli
Erick Petrucelli

Reputation: 14872

This is why bind exists.

$.each(all_objects, function(index) {{
    $("#saved-list").append("<li id='item" + index + "'><a href='#saved-route'>" + this.text + "</a></li>");
    $("#saved-list li").bind("click", function() {
        alert("Clicked on " + this.id);
    });
});

Also, this way is very easy to create different clicks for each item:

$("#saved-list li#item1").bind(...)
$("#saved-list li#item2").bind(...)
$("#saved-list li#item3").bind(...)

Upvotes: 0

Matt Ball
Matt Ball

Reputation: 359776

You're better off using .live() or .delegate() than worrying about creating a .click() handler on every element you create. Something like this:

$('#saved-list').delegate('li', 'click', function () {
    // do stuff on click
});

You only have to bind this click listener once, and it will work for every <li> that is a descendant of the element with ID saved-list.


If you do want to create a separate click handler for every <li> (I don't recommend this though) here's a nice way to do it:

$.each(all_objects, function() {
    var $a = $('<a/>', {
        href: '#saved-route',
        text: this.text
    });

    var $li = $('<li/>', {
        click: function () {
            // do stuff on click
        }
    }).append($a);

    $('#saved-list').append($li);
});

Upvotes: 16

megakorre
megakorre

Reputation: 2223

hello phil in jquery you can create dom object without adding them as text to the dom. this will give you a chans to manipulate them before they are added (and after to).

$("<li />")
  .append(
    $("<a />")
     .attr("href" ,"#saved-route")
     .text(this.text))
  .click(function() {
    // your click event
  })
  .appendTo("#saved-list");

Upvotes: 1

Domenic
Domenic

Reputation: 112817

Although I don't know what this $('#saved-list').refresh('listview') business is about, the following might be what you're looking for:

var listItemEls = all_objects.map(function () {
    return $('<li id="' + this.id + '"><a href="#saved-route">' + this.text + '</a></li>')
           .click(function (e) {
               // do whatever here
           })
           .get();
});

$('#saved-list').append(listItemEls);

Note that we avoid appending to the DOM until the last minute, because appending is expensive.

As other posters mention, using a delegated click handler is a better approach, generally. That would look something like

var listItemEls = all_objects.map(function () {
    return $('<li id="' + this.id + '"><a href="#saved-route">' + this.text + '</a></li>')
           .get();
});

$('#saved-list').append(listItemEls)
                .delegate("li", "click", function (e) {
                    // do stuff here.
                });

Upvotes: 0

T. Stone
T. Stone

Reputation: 19485

The .click function will bind a click handler function to any given element. As the comments in your code mention you can use the ID or other means to select the item after it's been DOM-ized but another way to do this is simply to reference the variable that contains the element you want to bind.

Example:

var li = $('<li />');
li.click(function() { ... });

This is probably somewhat stylistic, but one of the features of jQuery is that it is chainable. This means that jQuery function calls always return a reference to the wrapped set of jQuery itself. Using the chainable aspect, you could re-write the above code like so:

var list = $('#saved-list'); $.each(all_objects, function(i, item) {{
    list.append(
        $('<li />')
            .append(
                $('<a />').text(item.text)
            )
            .click(function() { ... } );
    );
});

Upvotes: 0

epascarello
epascarello

Reputation: 207501

$.each(all_objects, function() {{
    var list_route = "<li><a href='#saved-route'>" + this.text + "</a></li>";
    var newLi = $(list_route);
    newLi.click( function(){} ).attr("id","foo");
    //if you want the a
    newLi.find("a").click( function(){} ).attr("id","foo");

    $('#saved-list').append(newLi);              
});

Upvotes: 0

Grant Thomas
Grant Thomas

Reputation: 45083

You could use the live function, the downfall is that you might need some mechanism to determine exactly which of the li items that have been clicked:

$("#saved-list li").live('click', function() {
    //act on click
});

Upvotes: 1

lonesomeday
lonesomeday

Reputation: 237847

Don't.

Rather than binding a new event handler for each element (which could become quite expensive), bind a single event handler to #saved-list, which is a common ancestor. Event bubbling means that ancestor elements are notified of events on their descendants, so you can handle events there instead of on the originating element.

Something like this...

$.each(all_objects, function() {{
    var list_route = "<li><a href='#saved-route'>" + this.text + "</a></li>";
    $('#saved-list').append(list_route);      
});
$('#saved-list').delegate('li', 'click', function() {
    // do something here each time a descendant li is clicked
});

See delegate

Upvotes: 6

Bas Slagter
Bas Slagter

Reputation: 9929

Isn't it enough to add this in (or after) your current loop?

$('#saved-list li').click(function(){ alert('hello'); });

Upvotes: 1

Related Questions