0

I'm new to JavaScript and I'm making a simple app that accomplishes the following:

  1. Stores objects within an array
  2. Creates un-ordered list using a for loop
  3. Background changes to color stored in array when corresponding list item is clicked

I completed #1 and 2, but am having trouble with 3. I reviewed the answer here below for assistance, but it didn't help solve the issue.

// function creating un-ordered list
// it works!

function makeUL(songTitle) {
  var list = document.createElement('ul');
  for (var i = 0; i < songTitle.length; i++) {
    var track = songTitle[i];
    var trackName = track.name;
    var node = document.createElement("li");
    node.appendChild(document.createTextNode(trackName));
        list.appendChild(node);
    }
  return list;
}
document.getElementById('song-list').appendChild(makeUL(songTitle));

// here i call the list by tag name
var listItems = document.getElementById('song-list').getElementsByTagName('li');

// here's where it gets tricky
// the click works, but it always selects the last background-image in the array
for (var i = 0; i < listItems.length; i++) {
  listItems[i].addEventListener("click", function changeBackground() {
      document.body.style.background = "url(" + songTitle[i].background + ") no-repeat center center fixed";
      document.body.style.backgroundSize = "cover";
    })
  }
0

1 Answer 1

1

This is because you need to use closures. Update the for loop section with the provided closure.

// function creating un-ordered list
// it works!

function makeUL(songTitle) {
  var list = document.createElement('ul');
  for (var i = 0; i < songTitle.length; i++) {
    var track = songTitle[i];
    var trackName = track.name;
    var node = document.createElement("li");
    node.appendChild(document.createTextNode(trackName));
    list.appendChild(node);
  }
  return list;
}
document.getElementById('song-list').appendChild(makeUL(songTitle));

// here i call the list by tag name
var listItems = document.getElementById('song-list').getElementsByTagName('li');

// here's where it gets tricky
// the click works, but it always selects the last background-image in the array
for (var i = 0; i < listItems.length; i++) {
  (function (i) {
    listItems[i].addEventListener("click", function changeBackground() {
      document.body.style.background = "url(" + songTitle[i].background + ") no-repeat center center fixed";
      document.body.style.backgroundSize = "cover";
    });
  })(i);
}

The reason why this happens is, every time when the loop is run, i is incremented, but remember, the eventListener is not executed until you click. The conversion of i or usage happens only when you click. When you finally do everything, i will be at the length + 1 value.

But in case of closures, the inner i is separated fully from the outer i. The value is saved and sent in a closed environment and i values remain the same. This means, the i used inside the event handler is not the same i of the for loop.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.