skip to Main Content

I can’t addEventListener to my own element I get the error "TypeError: i.getAttribute is not a function at HTMLLIElement." I get

HTML CODE:

<ul>
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
</ul>

JS:

var items = document.getElementsByClassName('item');

for (i of items) {
    i.addEventListener("click", function(i) {
        i.classList.add("selected");
    })
}

ERROR:

TypeError: i.getAttribute is not a function
    at HTMLLIElement.

3

Answers


  1. i.getAttribute is not a function

    Because getAttribute is not a function on Events. And i is an Event:

    function(i) {
      //...
    }
    

    If your goal is to access this i:

    for (i of items) {
    

    Which, incidentally, should probably not be an implicit global and should explicitly declare the variable in the current scope:

    for (let i of items) {
    

    Then don’t shadow it with another variable of the same name. Either remove the Event reference entirely if you don’t intend to use it:

    function() {
      //...
    }
    

    Or, if you do intend to use it, give it a different name:

    function(e) {
      //...
    }
    
    Login or Signup to reply.
  2. You need to define i first.

    for (let i of items)
    

    hope this solve your issue

    Login or Signup to reply.
  3. Your reuse of the i variable within the event callback isn’t referencing the element as you think it is, therefore causing the error.

    This all stems from the way you are attempting to loop over the selected elements and how you are attempting to access them when you loop. getElementsByClassName() should rarely (if ever) be used and most certainly not if you are going to loop over the returned node list.

    And, if you use the correct modern approach to getting the references, you can then use the modern .forEach() method to iterate over the node list.

    Then, during each iteration, when you set up the event handler, you can refer to the current item with the argument specified within the callback function of the forEach() method.

    Finally, within the event handler, you can refer to the element that triggered the event in the first place with the this identifier.

    Here would be the modern way to do this:

    // No need to set up a variable to reference the selected nodes if you 
    // don't plan to reference the entire node list again. Instead, you can
    // just "chain" the .forEach() method on the resulting node list. 
    
    // Note that forEach() specifies (with the first argument) the identifier
    // that each item in the node list should be accessible as (element in this case):
    document.querySelectorAll(".item").forEach(function(element){
      // Then you can access each item during the loop with the variable from forEach():
      element.addEventListener("click", function(event){
         // And within the event handler, this will refer to the element that triggered
         // the event in the first place. Using this, you can access properties of the element
         // directly with "dot notation".
         this.classList.add("active");
         // Just for fun:
         console.log("You initiated the " + event.type + " event of the " + this + " element, which has the: " + this.className + " class(es).");
      });
    });
    .active { color:red; }
    <ul>
        <li class="item">test</li>
        <li class="item">test</li>
        <li class="item">test</li>
        <li class="item">test</li>
        <li class="item">test</li>
    </ul>

    And, if you are wanting only the most recently clicked item to have the active class, you need to loop over all the items upon the click of any of them and remove the class first. Then, you can apply it to only the one that was just clicked. That would look like this:

    let items = document.querySelectorAll(".item");
    
    items.forEach(function(element){
    
      element.addEventListener("click", function(event){
         // First, loop over all the node list elements and remove the active class
         items.forEach(function(item){
           item.classList.remove("active");
         });
         
         // The apply the active class to only the one that just got clicked:
         this.classList.add("active");
      });
      
    });
    .active { color:red; }
    <ul>
        <li class="item">test</li>
        <li class="item">test</li>
        <li class="item">test</li>
        <li class="item">test</li>
        <li class="item">test</li>
    </ul>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search