Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

How to Extend the Quiz - Add support for multiple choices.

Hi,

I'm interested in extending the quiz to allow each question to have 2 or more choices. Upon experimenting with the completed project, I noticed that the first item in the questions array must have the same number of questions as buttons. If there are three or more choices on the first array item, the DOM doesn't dynamically generate buttons or display the choices.

I'd like to implement this functionality to allow the number of buttons to correspond with any number of choices on the page. If anyone has any suggestions on how to add some dynamic button functionality for each question, I'm interested in a proposed solution or at least a good reference point about where to begin implementing it.

Andrew Chalkley, this might be a good extra credit feature for this video. I'm curious as to which JS file do you think would make sense for this functionality. Also, is this something that you'd recommend doing in vanilla JS or using jQuery?

Thanks, Jeremy

3 Answers

Andrew Chalkley
STAFF
Andrew Chalkley
Treehouse Guest Teacher

You can use either. It's a matter of preference. If you can do it in vanilla JS, great. That saves the extra download of the jQuery library. Either way it's fine.

You can create elements with document.createElement in plain old JavaScript or $('<button />') in jQuery.

Andrew Chalkley , I developed a solution that seems to work. I was wondering if you could provide a code review for the method and/or any improvements that you'd make. I feel like it could be more DRY, but on the upside it works. As mentioned previously, this may make a cool extra credit project in the teacher's notes for the Quiz project. Thanks!

All the additions were done in Quiz_UI.js

I updated the displayChoices method to include a call to the removeChoices method:

    displayChoices: function () {
      var choices = quiz.getCurrentQuestion().choices;
      var buttons = document.getElementsByTagName('button');

      for(var i = 0; i < choices.length; i++) {
          this.populateIdWithHTML("choice" + i, choices[i]);
          this.guessHandler("guess" + i, choices[i]);
      }

      this.removeChoices();

    },

From there, I built a method from scratch called removeChoices. This method is basically meant to help remove buttons and list items. This way, the Quiz UI adapts to questions that may have more choices than others.

    removeChoices: function () {
      var buttons = document.getElementsByTagName('button');
      var listItems = document.getElementsByTagName('li');

      var numOfChoices = quiz.getCurrentQuestion().choices.length;;
      var numOfButtons = buttons.length;

      // Remove old list items/buttons, if current question has less than previous      
      if (numOfChoices < numOfButtons) {

      var btnDifference = numOfButtons - numOfChoices;

        for(var i = 0; i < btnDifference ; i++) {
          var elementID = numOfButtons - 1;
          var oldButton = buttons[elementID - i];
          var oldListItem = listItems[elementID - i];

          oldButton.parentNode.removeChild(oldButton);
          oldListItem.parentNode.removeChild(oldListItem);
        }
      }
    },
Andrew Chalkley
Andrew Chalkley
Treehouse Guest Teacher

That's awesome. I like how you separated your code. It's very readable.

Here's something you could do...

  1. Create a function called createElement() that takes in a string of tagName and returns the newly created element.
  2. Set a variable to the expression elementID - i as something understandable.
  3. You could also create a function called removeChild() that removes the child element from the parentNode.

Hey Andrew Chalkley, I actually had to rework what I provided to you. My method was useful for only one condition - When the number of buttons was greater than the choices in the array for that question. So... I refactored a bit and created a new method altogether. So far, it seems to work for any number of choices for a given question.

Here's what I ended up doing:

Updated displayNext method to include new Method updateButtons():

    displayNext: function () {
      if (quiz.hasEnded()) {
        this.displayScore();
      } else {
        this.displayQuestion();
        this.updateButtons();
        this.displayChoices();
        this.displayProgress();
      }
    },

From there, I created a pretty heavy method called updateButtons:

 updateButtons: function () {
      var choices = quiz.getCurrentQuestion().choices;
      var buttons = document.getElementsByTagName('button');
      var listItems = document.getElementsByTagName('li');

      var numOfChoices = quiz.getCurrentQuestion().choices.length;;
      var numOfButtons = buttons.length;

      // Remove old choices/buttons, if current question has less      
      if (numOfChoices < numOfButtons) {

      var btnDifference = numOfButtons - numOfChoices;

        for(var i = 0; i < btnDifference ; i++) {
          var elementID = numOfButtons - 1;
          var oldButton = buttons[elementID - i];
          var oldListItem = listItems[elementID - i];

          oldButton.parentNode.removeChild(oldButton);
          oldListItem.parentNode.removeChild(oldListItem);
        }

      } else if (numOfChoices > numOfButtons) {

        // Calculate difference to determine how many buttons to update
        var btnDifference = numOfChoices - numOfButtons;

        // Find current choice index location
        var currentIndex = numOfChoices - btnDifference - 1;

        for(var i = 0; i < btnDifference ; i++) {

          currentIndex ++;

          // Create empty DOM elements  
          var newListItem = document.createElement("li");
          var newButton = document.createElement("button");
          var newButtonText = document.createTextNode("Select Answer");

          // Set attributes and append text
          newButton.setAttribute("id", "guess" + currentIndex);
          newButton.setAttribute("class", "btn--default");
          newButton.appendChild(newButtonText);

          newListItem.setAttribute("id", "choice" + currentIndex);

          // DOM reference point
          var container = document.getElementById("choice-container");

          // Add to container  
          container.appendChild(newListItem);
          container.appendChild(newButton);

          this.populateIdWithHTML("choice" + i, choices[i]);
          this.guessHandler("guess" + i, choices[i]);
        }                                        

      } else {
        return true;
      }
    },

There's one thing that's really bothering me and I don't know if this really matters or not. I've noticed that I'm calling the same variables in different methods. When this starts happening, is it best practice to move them out of their individual methods and into the global namespace?

I'm also curious if you think there's a much shorter or more efficient way to refactor the updateButtons method.

Thanks!