26

I'm defining a list of items from a JSON file and displaying that list on a web page. The intention is for users to click on any item in that list to display more information on that item (information that is held in the same JSON file).

All items of that list are members of the same class, and each has a unique id defined from the JSON file. The HTML looks something like this:

<ul>
   <li class="menu" id="bob">Robert Smith</li>
   <li class="menu" id="jane">Jane Doe</li>
   <li class="menu" id="sue">Susan Carter</li>
</ul>

I was planning to use something along the lines of

var userSelection = document.getElementsByClassName('menu');

in concert with

userSelection.addEventListener('click', myFunctionToPrintDetails());

but I am not sure how to pass the id from the event listener to the print function in order to determine which details to print.

I have a lot of experience with HTML/CSS but very little with JSON/AJAX, so possible I'm going about this completely bass-ackwards. If there is a more appropriate way to get this done I'm open to the feedback.

I tried both answers but neither worked. When I log userSelection.length to the console I get 0; when I log userSelection I get:

HTMLCollection(0)
> sue: li#sue.menu
length: 3
> jane: li#jane.menu
> bob: li#bob.menu
> 0: li#bob.menu
> 1: li#jane.menu
> 2: li#sue.menu
Michael
  • 519
  • 1
  • 4
  • 15
  • 1
    Add the listener to _each_ element in the `userSelection` collection. Add the listener using the function name, `myFunctionToPrintDetails`, not a call to the function. Inside the handler the element clicked is the `this` value, assuming you have not used a lambda expression (arrow function) for the handler. – traktor Feb 07 '17 at 01:56

5 Answers5

22

codepen demo

var userSelection = document.getElementsByClassName('menu');

for(var i = 0; i < userSelection.length; i++) {
  (function(index) {
    userSelection[index].addEventListener("click", function() {
       console.log("Clicked index: " + index);
     })
  })(i);
}

As pointed out by @torazaburo in the comments, you can use the following if the browsers you want to support are complying with ECMAScript 6 (ES6), the newest version of JavaScript.

var userSelection = document.getElementsByClassName('menu');

for(let i = 0; i < userSelection.length; i++) {
  userSelection[i].addEventListener("click", function() {
    console.log("Clicked index: " + i);
  })
}
Der
  • 727
  • 7
  • 20
  • 2
    where is `index` defined? What is its value after the loop has finished? – traktor Feb 07 '17 at 02:00
  • It was a typo, fixed now. – Der Feb 07 '17 at 02:01
  • 1
    There's still the "infamous javascript loop issue" - `i` is set to `userSelection.length` when the click occurs. See http://stackoverflow.com/q/750486/5217142 – traktor Feb 07 '17 at 02:03
  • Thanks for pointing it out, you're right. I added an IIFE to fix the issue. – Der Feb 07 '17 at 02:21
  • I tried both solutions to but wasn't getting anywhere. userSelection.length to the console I get 0, but when I print userSelection I get: HTMLCollection(0) > sue: li#sue.menu length: 3 > jane: li#jane.menu > bob: li#bob.menu > 0: li#bob.menu > 1: li#jane.menu > 2: li#sue.menu – Michael Feb 07 '17 at 02:58
  • 1
    Are you sure you called `var userSelection = document.getElementsByClassName('menu');` before calling `userSelection.length`? If so, compare your result with a [codepen implementation](http://codepen.io/bubblesphere/pen/PWBQRo) of my solution – Der Feb 07 '17 at 04:05
  • 3
    All of this rigmarole with IIFE's in the loop is unnecessary with `let`. –  Feb 07 '17 at 04:11
  • In the second form you might as well use `const` for the `userSelection`: `const userSelection = document.getElementsByClassName('menu');` See this [explanation](https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/) or ask google, if you don't know why. – Holger Böhnke Oct 05 '22 at 03:41
7

Here's how I would do it. I would first create an array using Object values.

const userSelection = Object.values(document.getElementsByClassName('menu'));

Then I would loop through them using a foreach.

userSelection.forEach(link => {
    link.addEventListener(event => {
        event.preventDefault();
        // more code here
    });
});

Easy peasy!

Kegan VanSickle
  • 239
  • 5
  • 3
6

You can get the id from the element that responded to the click event with this.id:

var items = document.getElementsByClassName('menu');
for (var i = 0; i < items.length; i++) {
  items[i].addEventListener('click', printDetails);
}

function printDetails(e) {
  console.log("Clicked " + this.id);
}
<ul>
   <li class="menu" id="bob">Robert Smith</li>
   <li class="menu" id="jane">Jane Doe</li>
   <li class="menu" id="sue">Susan Carter</li>
</ul>
tklg
  • 2,572
  • 2
  • 19
  • 24
  • I tried both solutions to but wasn't getting anywhere. userSelection.length to the console I get 0, but when I print userSelection I get: HTMLCollection(0) > sue: li#sue.menu length: 3 > jane: li#jane.menu > bob: li#bob.menu > 0: li#bob.menu > 1: li#jane.menu > 2: li#sue.menu – Michael Feb 07 '17 at 02:56
0
<ul id = "menuList">
   <li class="menu" id="bob">Robert Smith</li>
   <li class="menu" id="jane">Jane Doe</li>
   <li class="menu" id="sue">Susan Carter</li>
</ul>


// JS - Event Bubbling
const menuList= document.getElementById("menuList");
menuList.addEventListener("click", function (e) {
        console.log(e.target); // log clicked element 
});
0

Here is a shorter version I'm using all the time:

[...(document.getElementsByClassName('__MYCLASSNAME__'))].forEach(d => {
  d.addEventListener('click', MYFUNCTION__);
})
Marcus
  • 5,407
  • 3
  • 31
  • 54
stallingOne
  • 3,633
  • 3
  • 41
  • 63