85

I have a list view for delete id. I'd like to add a listener to all elements with a particular class and do a confirm alert.

My problem is that this seems to only add the listener to the first element with the class it finds. I tried to use querySelectorAll but it didn't work.

var deleteLink = document.querySelector('.delete');

deleteLink.addEventListener('click', function(event) {
    event.preventDefault();

    var choice = confirm("sure u want to delete?");
    if (choice) {
        return true;
    }
});

List:

<?php 
    while($obj=$result->fetch_object())
    {
        echo '<li><a class="delete" href="removeTruck.php?tid='.$obj->id.'">'.$obj->id.'</a>'
            . '<a href="#" class="delete"></a>
                      </li>'."\n";
    }
    /* free result set */
    $result->close();       
    $mysqli->close();
?>
u32i64
  • 2,384
  • 3
  • 22
  • 36
user1246950
  • 1,022
  • 2
  • 10
  • 19
  • 2
    Why tagged it with jQuery? BTW, you'd have better to delegate event instead of setting for each .delete element own handler – A. Wolff Feb 11 '14 at 11:19

5 Answers5

164

You should use querySelectorAll. It returns NodeList, however querySelector returns only the first found element:

var deleteLink = document.querySelectorAll('.delete');

Then you would loop:

for (var i = 0; i < deleteLink.length; i++) {
    deleteLink[i].addEventListener('click', function(event) {
        if (!confirm("sure u want to delete " + this.title)) {
            event.preventDefault();
        }
    });
}

Also you should preventDefault only if confirm === false.

It's also worth noting that return false/true is only useful for event handlers bound with onclick = function() {...}. For addEventListening you should use event.preventDefault().

Demo: http://jsfiddle.net/Rc7jL/3/


ES6 version

You can make it a little cleaner (and safer closure-in-loop wise) by using Array.prototype.forEach iteration instead of for-loop:

var deleteLinks = document.querySelectorAll('.delete');

Array.from(deleteLinks).forEach(link => {
    link.addEventListener('click', function(event) {
        if (!confirm(`sure u want to delete ${this.title}`)) {
            event.preventDefault();
        }
    });
});

Example above uses Array.from and template strings from ES2015 standard.

Community
  • 1
  • 1
dfsq
  • 191,768
  • 25
  • 236
  • 258
  • i tryed that but it didnt work i did for(int i=0...)deletelink[i].addEventListener('click', function(event) { event.preventDefault(); var choice = confirm("sure u want to delete?"); if (choice) { return true; } }); – user1246950 Feb 11 '14 at 11:22
  • Take a look at the demo I've set up. – dfsq Feb 11 '14 at 11:23
  • 1
    i guess i did same thing worng when i did it the first time thx u :)) – user1246950 Feb 11 '14 at 11:26
  • 3
    Another neat trick is using the [spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) operator which works on array-like objects. So instead of `Array.from(deleteLinks)`, you can do `[...deleteLinks]`. I think `Array.from` is clearer, but just a neat thing worth pointing out. – Saad Jul 20 '16 at 06:08
  • why not directly using `deleteLinks.forEach` instead of `Array.from(deleteLinks).forEach` ? – Olivier Boissé Aug 31 '19 at 18:00
  • 1
    @OlivierBoissé Because `forEach` is a method of Array. And when this answer was written NodeList didn't implement it. And even now it will not work in older browsers like IE something (11?). – dfsq Sep 01 '19 at 22:07
68

The problem with using querySelectorAll and a for loop is that it creates a whole new event handler for each element in the array.

Sometimes that is exactly what you want. But if you have many elements, it may be more efficient to create a single event handler and attach it to a container element. You can then use event.target to refer to the specific element which triggered the event:

document.body.addEventListener("click", function (event) {
  if (event.target.classList.contains("delete")) {
    var title = event.target.getAttribute("title");

    if (!confirm("sure u want to delete " + title)) {
      event.preventDefault();
    }
  }
});

In this example we only create one event handler which is attached to the body element. Whenever an element inside the body is clicked, the click event bubbles up to our event handler.

jackvsworld
  • 1,453
  • 15
  • 17
48

A short and sweet solution, using ES6:

document.querySelectorAll('.input')
      .forEach(input => input.addEventListener('focus', this.onInputFocus));
hodgef
  • 1,416
  • 2
  • 19
  • 31
7

You have to use querySelectorAll as you need to select all elements with the said class, again since querySelectorAll is an array you need to iterate it and add the event handlers

var deleteLinks = document.querySelectorAll('.delete');
for (var i = 0; i < deleteLinks.length; i++) {
    deleteLinks[i].addEventListener('click', function (event) {
        event.preventDefault();

        var choice = confirm("sure u want to delete?");
        if (choice) {
            return true;
        }
    });
}
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • 5
    This answer is a bit old (3.5 years old), but I must rectify that `querySelectorAll` doesn't return a `Array`, but a `NodeList`, which looks like a `Array` but doesn't have the same methods. – NemoStein Aug 23 '17 at 16:43
2

(ES5) I use forEach to iterate on the collection returned by querySelectorAll and it works well :

document.querySelectorAll('your_selector').forEach(item => { /* do the job with item element */ });
Foxlab
  • 564
  • 3
  • 9