views:

838

answers:

4

I need to pick up list items from an list, and then perform operations like adding event handlers on them. I can think of two ways of doing this.

HTML:

    <ul id="list">
       <li id="listItem-0"> first item </li>
       <li id="listItem-1"> second item </li> 
       <li id="listItem-2"> third item </li> 
    </ul>
  1. Using the IDs-

    for(i=0;i<3;i++)
    {
       document.getElementById("listItem-"+i).addEventListener("click",foo,false);
    }
    
  2. Using childNodes property-

    for(i=0;i<3;i++)
    {
      document.getElementById("list").childNodes[i]
         .addEventListener("click",foo,false);
    }
    

The reason i'm using the first approach is that in the function foo, if I want the index at which the item is located in the list, i can do it by splitting the id -

    function foo()
    {
      tempArr = this.id.split('-');      
      index = tempArr[tempArr.length-1]; // the last element in the array
    }

I can't think of a way of doing it by using the second method, that is, without using the id naming scheme.

The questions:

  1. How do I get the index using the second method or any better method
  2. Are there some very bad affects of following the first method ?
+3  A: 

You can avoid adding event handlers to each list item by adding a single event handler to the containing element (the unordered list) and leveraging the concept of event bubbling. In this single event handler, you can use properties of the event object to determine what was clicked.

It appears that you are wanting to map data in an array to list items. While parsing out an array index out of the list item id would work, another approach would be to store a "key" value on the list item as an expando, and use javascript object properties to do a lookup for your data.

Partial Example:

<li key="myKey">

//oData is a object (eg. var oData = {};) that has been populated with properties
//whose value is the desired data (eg. oData["myKey"] = 123;)

alert(oData[event.srcElement.key]); // alerts 123

As far as bad effects of the first technique you showed, one bad effect is that with many list items you end up with many event handlers being defined, which at some point would have an impact on performance.

Also note that you may be unintentionally creating a global var in your loops by omitting the var keyword for "i".

J c
+1  A: 

If you opt to use jQuery it comes as simple as:

$('ul#list li').click(function () {
    var i = this.id.split('-').pop();
    alert( i );
});
Marko Dumic
+1  A: 

Maybe something similar to:

var lis = document.getElementById("list").getElementsByTagName("li");

for (var i = 0, li; li = lis[i]; ++i) {
  li.addEventListener("click", (function(pos) {
    return function() {
      alert(pos);
    };
  })(i), false);
}

Or, with some inspiration from J cs answer and custom data attributes:

var lis = document.getElementById("list").getElementsByTagName("li");

for (var i = 0, li; li = lis[i]; ++i) {
  li.setAttribute("data-index", i); // Or whatever value you want...
  li.addEventListener("click", function() {
    alert(this.getAttribute("data-index"));
 }, false);
}
cic
A: 

As stated before, if you want to retrieve the list of li, you should use getElementsByTagName on your ul, because childNodes might retrieve some text nodes as well as li nodes.

Now if what you need is to use the index in the event handler, you might be better directly using a closure in order to reuse the loop variable inside the event handler.

var lis = document.getElementById("list").getElementsByTagName("li");

for( var i = 0, l = lis.length; i < l; ++i )
{
    (function(){

        // As a new variable will be created for each loop, 
        // you can use it in your event handler  
        var li = lis[i]; 

        li.addEventListener("click", function() 
        {
            li.className = "clicked";

        }, false);

    })();
}

But you may consider event delegation for this purpose. You only have to attach an event handler to the parent element and find the clicked element using the target attribute.

document.getElementById("list").addEventListener("click", function(event)
{
    var li = event.target;
    if( li.nodeName.toLowerCase() == "li" )
    {
        ...
    }
}, false);
Vincent Robert