Skip to content

Array.from() not converting nodeList into an array

I’ve created a nodeList of list elements using createElement(). Then I’ve used Array.from() to convert the nodeList in question into array I can iterate over. I want to apply a different width according to the value of the index. If index is even width of 300px else width of 500px. However, the console returns “Cannot read property ‘style’ of undefined at bottlesOnTheWall”.

I’ve also used […] to turn my nodeList into an array but without success either. My guess is that is not a nodeList in the first place, which means it can’t be converted into an array. At least not using either of these approaches.

I was wondering if someone could point out where I’ve gone wrong and fix my code. I’ve been spending more time that’s healthy trying to do so.

const bottles = document.getElementById("bottles");
count = 99;
var myArray = [];
var j = 0;

function bottlesOnTheWall() {
  while (count > 0) {
    if(count > 2) {
      myArray.push(`${count} bottles of beer on the wall, ${count} bottles of beers. Take one down and pass it around, ${count - 1} bottles of beer on the wall`)
    } else if (count === 2) {
      myArray.push(`${count} bottles of beer on the wall, ${count} bottles of beers. Take one down and pass it around, ${count - 1}bottle of beer on the wall`)
    } else if (count === 1) {
      myArray.push(`${count} bottle of beer on the wall, ${count} bottles of beers. No more bottle of beer on the wall`)
    } else {
      myArray.push(`No more bottles of beer on the wall. No more bottles of beer.  Go to the shop and buy some ${count} more.`)
    }
    count--
  }
  while (j < myArray.length) {
    var liElement = document.createElement("li");
    var text = document.createTextNode(myArray[j]);
    liElement.appendChild(text);
    bottles.appendChild(liElement);
    var bottlesArray = Array.from(bottles);
    if(j % 2) {
      bottlesArray[j].style.width = "300px";
    } else {
      bottlesArray[j].style.width = "500px";
    }
    j++;
  }
}
bottlesOnTheWall();
#bottles {
  line-height: 2;
  letter-spacing: 3px;
}

/* ul {
  list-style-image: url('beer.png');
} */

body {
  background: #FFF8DC;
}

li {
  max-width: 500px;
  margin: auto;
  margin-bottom: 10px;
}

ul li {
  background: #FFEBCD;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>bottles on the wall</title>
    <link rel="stylesheet" href="index.css">
  </head>
  <body>
    <ul id="bottles" ></ul>
    <script src="index.js" type="text/javascript"></script>

  </body>
</html>

Answer

Array.from needs a variable with an implemented Symbol.iterator. A single element does not have it, but a list of elements, which is not given here.

Then you have some more issues:

  • Global variables, but only used in a single function. Just move all varaibles inside of the function.

  • Take a parameter for count.

  • Take a single loop without collecting all texts first in an array and then iterate again for creating elements. The only purpose is to use a layer model to separate the data collection from the presentation layer.

  • Finally take a conditional (ternary) operator ?: for width.

function bottlesOnTheWall(count) {
    const bottles = document.getElementById("bottles");
    
    while (count > 0) {
        let text;

        if (count > 2) text = `${count} bottles of beer on the wall, ${count} bottles of beers. Take one down and pass it around, ${count - 1} bottles of beer on the wall`;
        else if (count === 2) text = `${count} bottles of beer on the wall, ${count} bottles of beers. Take one down and pass it around, ${count - 1} bottle of beer on the wall`;
        else if (count === 1) text = `${count} bottle of beer on the wall, ${count} bottles of beers. No more bottle of beer on the wall`;
        else text = `No more bottles of beer on the wall. No more bottles of beer.  Go to the shop and buy some ${count} more.`;

        count--;

        const liElement = document.createElement("li");
        liElement.appendChild(document.createTextNode(text));
        bottles.appendChild(liElement);
        liElement.style.width = count % 2
            ? "300px"
            : "500px";
    }
}

bottlesOnTheWall(99);
#bottles { line-height: 2; letter-spacing: 3px; }
body { background: #FFF8DC; }
li { max-width: 500px; margin: auto; margin-bottom: 10px; }
ul li { background: #FFEBCD; }
<ul id="bottles"></ul>