1
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Add Multiple Choice Question</title>
        <link rel="stylesheet" href="./multiple.css">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
        <script src="./multiple.js"></script>
    </head>
    <body>
        <script src="./multiple.js"></script>
        <div class="multiple">
            
            <div class="question">
                <label id="question-label" for="question">Question</label>
                <textarea name="question-area" id="qarea" cols="38" rows="5"></textarea>
            </div>
            <div class="answer">
                <label class="answer-label" for="answer">Choice-1 </label>
                <input class="answer-inp" type="text">
                <i class="fa-solid fa-circle-plus fa-xl" id="icon-add" onclick="add_more_field()"></i>
            </div>
            
           <!-- <div class="icon"></div>-->
    
    
        </div>
        
    </body>
    </html>
function add_more_field(){
        
       html = '<div class="answer">\
       <label class="answer-label" for="answer">Choice-1 </label>\
       <input class="answer-inp" type="text">\
       <i class="fa-solid fa-circle-plus fa-xl" id="icon-add" onclick="add_more_field()"></i>\
    </div>'
    
    var add = document.getElementsByClassName("multiple");
    add.appendChild(html);
    }

I want to write a javascript code that creates the same answer div when the user presses the + icon again. This will be the answers to a multiple choice question and the number of answers needs to be increased dynamically. I wrote the function, but it does not create a new answer div when I click on the icon. How can I do that?

3 Answers 3

0

here you can try this logic :

 let html =
        '<div class="answer">\
         <label class="answer-label" for="answer">Choice-1 </label>\
         <input class="answer-inp" type="text">\
         <i class="fa-solid fa-circle-plus fa-xl" id="icon-add" onclick="add_more_field()"></i>\
      </div>';

      function add_more_field() {
        var add = document.querySelector(".multiple");
        add.innerHTML += html;
      }
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Add Multiple Choice Question</title>

    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css"
    />
  </head>
  <body>
    <div class="multiple">
      <div class="question">
        <label id="question-label" for="question">Question</label>
        <textarea name="question-area" id="qarea" cols="38" rows="5"></textarea>
      </div>
      <div class="answer">
        <label class="answer-label" for="answer">Choice-1 </label>
        <input class="answer-inp" type="text" />
        <i
          class="fa-solid fa-circle-plus fa-xl"
          id="icon-add"
          onclick="add_more_field()"
        ></i>
      </div>

      <!-- <div class="icon"></div>-->
    </div>

  </body>
</html>

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

3 Comments

document.getElementsByClassName(".multiple"). It will give you node array so, you have to iterate or get the index directly of node.
All of the new answers will have a Choice-1 label. How would you resolve that?
we can create counter variable for that choice - $counter and each time on button click we can increase this counter to choice -1 , choice -2 and so on.
0

There are a few issues with the code as it stands:

  1. Your script call appears before any of the DOM elements have rendered so using a DOM method to get those elements will return undefined.

  2. getElementsByClassName returns "an array-like object of child elements" which is the wrong method to use if you're only looking to get one element - in this case your container element - so use querySelector instead.

  3. appendChild won't work either because it requires the thing that's being appended to be a node (created most likely with document.createElement), and your HTML is a string. insertAdjacentHTML is a modern approach to attaching HTML to a page.

  4. Each new HTML string added to the DOM will have a "Choice-1" label. Presumably you'd want that to be incremented each time. Pass in the count of the number of current answers to the function building your HTML, and use a template string to allow you to update the count in the string itself.

  5. Ids must be unique. At the moment all of your icons in your newly-added HTML will have the same id which will only cause problems later. Remove the id. Wrap the icon in a button element so that it's focusable, and add a new class to the button (for example add-answer).

  6. You shouldn't really be using inline JS in 2022. Look instead at using event delegation. Attach one listener to the containing element, and have that listen to events from its children (even the dynamically added elements in the new HTML) as they bubble up the DOM. Depending on what element fired the event you can handle it appropriately.

// Cache the container element, and add a listener to it
// This will catch the events from its children and handle them
// appropriately
const container = document.querySelector('.multiple');
container.addEventListener('click', handleClick);

// The buildHTML function accepts a count, and returns some HTML
// using a template string. Note you can use `count` in the string,
// and increment it.
function buildHTML(count) {
  return `
    <div class="answer">
      <label class="answer-label" for="answer">
        Choice-${count + 1}
      </label>
      <input class="answer-inp" type="text">
      <button type="button" class="add-answer">
        <i class="fa-solid fa-circle-plus fa-xl"></i>
      </button>
    </div>
  `;
}

// `e` is the event the listener catches
function handleClick(e) {
  
  // If the clicked element (`e.target`)
  // has an `add-answer` class (ie the button)...
  if (e.target.matches('.add-answer')) {
  
    // ...get the current number of answers by selecting
    // all the elements with the `answer` class, and getting its length
    const count = container.querySelectorAll('.answer').length;

    // Build some HTML passing in that count value
    const html = buildHTML(count);
    
    // Insert that HTML on to the end of the container
    container.insertAdjacentHTML('beforeend', html);
  }
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" rel="stylesheet"/>
<div class="multiple">
  <div class="question">Question</div>
  <div class="answer">
    <label class="answer-label" for="answer">
      Choice-1
    </label>
    <input class="answer-inp" type="text">
    <button type="button" class="add-answer">
      <i class="fa-solid fa-circle-plus fa-xl"></i>
    </button>
  </div>
</div>

Comments

0

getElementsByClassName as the function name suggests, returns a list of elements and not just one. Plus you are trying to appendChild() passing an html string instead of an element object.

This code achieves something like what you expected to get, getting the container element through an id instead of its class and concatenating the new html to its innerHTML property instead of appending the child element that would require a bigger effort to build up.

(at the time I was writing this answer) I see that in the meantime you received this same exact answer from another user but I decided to keep mine anyway because it adds more context

let choiceNumber = 1;

function addNewField(passedLabel) {

  choiceNumber++;
  if(passedLabel === undefined)
     choiceLabel = `Choice-${choiceNumber}`;
  else
     choiceLabel = passedLabel;

  html = `
    <div class="answer">
      <label class="answer-label" for="answer">${choiceLabel}</label>
      <input class="answer-inp" type="text">\
      <i class="fa-solid fa-circle-plus fa-xl" id="icon-add" onclick="add_more_field()"></i>
    </div>`;

  var add = document.getElementById("multiple");
  add.innerHTML += html;
}
<body>

  <div id="multiple">

    <div class="question">
      <label id="question-label" for="question">Question</label>
      <textarea name="question-area" id="qarea" cols="38" rows="5"></textarea>
    </div>
    
    <div class="answer">
      <label class="answer-label" for="answer">Choice-1</label>
      <input class="answer-inp" type="text">
      <i class="fa-solid fa-circle-plus fa-xl" id="icon-add" onclick="add_more_field()"></i>
    </div>

    <!-- <div class="icon"></div>-->
  </div>

  <button type="button" onclick="addNewField();">Add</button>

</body>

2 Comments

All of the new answers will have a Choice-1 label. How would you resolve that?
you already accepted the other answer for good reasons.. by the way I updated mine with the strategy to take care of the incremental number for the choice label

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.