findnate
findnate

Reputation: 13

More efficient way to create a new element JS(Dunno what the title should called)

Im new to JavaScript, I'm using vanilla js , html and css to survey website where user can add question, and option as well, when they click a button it will create a container which they can fill with the 'tag:textarea'. I'm using the 'createElement()', 'setAttribute()' method for creating a new container. But since each container has so many nested nodes or components, I have write a tons of boilerplate code to create a container, plus I have to change the id of some element with the nested 'Hell' , It become so hard to managed. My html code is Below:

<div id="create-question-container-id"              class="create-question-container" >
  <div id="create-question-textarea-container-id_0" class="create-question-textarea-container">
    <textarea id="create-question-textarea-id" 
              name="textareaquestion"               class="create-question-textarea" 
              cols="50" row="1" style="font-size: 20pt;">
      Untitled Question
    </textarea>
    <div id="create-answer-textarea-container-id-0" class="create-answer-textarea-container">
      <div  id="create-answer-N-container-id-0-0"   class="create-answer-N-container">
        <div                                        class="create-qeuestion-radio-outer-circle" > 
           <div                                     class="create-qeuestion-radio-innner-circle"></div>
        </div>
        <textarea id="opt1" name="textareaopt"      class="create-answer-textarea" 
                  placeholder="opt1" style="font-size: 14pt;">
        </textarea>
      </div>
    </div>
    <button onclick="addOption()"                   class="create-add-answer-btn" >
      Add Option
    </button>  
  </div>
</div>
<div                                                class="create-main-container-submit-button">
  <button onclick="clonecontainer()"                class="create-main-submit-btn" >
    Submit
  </button>
</div>

And this is the Javascript Code from hell:

var answer_container = document.getElementsByClassName('create-answer-textarea-container');
//increment this one 
var answer_container_id = document.getElementById('create-answer-textarea-container-id-0');

var lastChildAnswer = answer_container[answer_container.length - 1];
var asnwer_container_list = answer_container_id.childNodes[answer_container.length];

//id of container wrapping around the question and the options named questionasnwcontain(this need to increment id)
var question_answer_container_list = document.getElementById('create-question-textarea-container-id_0')

//id of container wrapping around the questionasnwcontain(main container)
var all_question_asnwer_container = document.getElementById('create-question-container-id');

var all_question_asnwer_container_class_Name = document.getElementsByClassName('create-question-container');

var all_counter = all_question_asnwer_container.childElementCount -1;


function clonecontainer() {
  var dashed_delimeter = '-';
  var lastChildContainer =  answer_container_id.lastElementChild.getAttribute('id')
  var trimlastChild =  answer_container_id.lastElementChild.lastElementChild.getAttribute('id')
  var delimeter = 't'
  var trimlastChildSubString = trimlastChild.split(delimeter);
  var lastChildString = trimlastChildSubString[1]
  //split lastChildContainer 
  var splitted_lastChildContainer = lastChildContainer.split(dashed_delimeter)[5];
  var new_id_spliited_lastChildContainer = 'create-answer-N-container-id-' + ((parseInt(splitted_lastChildContainer)) + (all_question_asnwer_container.childElementCount -1) ) + '-0';

  //get the cloned container's child id for incremental
  var last_main_container_child = all_question_asnwer_container.lastElementChild.getAttribute('id');
  var trimlast_id = seperateWithDelimeter(last_main_container_child);
  var trimlast_new_id ='_' + (parseInt(trimlast_id[1]) +  (all_question_asnwer_container.childElementCount -1) );
  console.log(trimlast_new_id)

  //(fixed)
  var last_cloned_delimeter = '_';
  var last_cloned_container = all_question_asnwer_container.childNodes[all_question_asnwer_container_class_Name.length].getAttribute('id')
  var splitted_cloned_container = last_cloned_container.split(last_cloned_delimeter);
  var new_splitted_cloned_container_id = 'create-answer-textarea-container-id-' + ((parseInt(splitted_cloned_container[1])) + (all_question_asnwer_container.childElementCount ))  ;
  console.log(new_splitted_cloned_container_id)
  //new div fr asnwer container 
  var new_div_fr_answer_container_id = document.createElement('div');
  new_div_fr_answer_container_id.setAttribute('class' , 'create-question-textarea-container');
  new_div_fr_answer_container_id.setAttribute('id' ,'create-question-textarea-container-id' + trimlast_new_id)
  console.log(new_div_fr_answer_container_id)

  //new question text area
  var new_textarea_fr_create_question_textarea_id = document.createElement('textarea');
  new_textarea_fr_create_question_textarea_id.setAttribute('class' , 'create-question-textarea');
  new_textarea_fr_create_question_textarea_id.setAttribute('id' , 'create-question-textarea-id')
  new_textarea_fr_create_question_textarea_id.setAttribute('cols' , 50);
  new_textarea_fr_create_question_textarea_id.setAttribute('row' , 1);
  new_textarea_fr_create_question_textarea_id.style.fontSize = '20pt';

  //new for option text area wrrapeer 
  var new_option_text_area_wrapper = document.createElement('div');
  new_option_text_area_wrapper.setAttribute('class' , 'create-answer-textarea-container');
  new_option_text_area_wrapper.setAttribute('id' , new_splitted_cloned_container_id);

  var new_option_N_text_area_wrapper = document.createElement('div');
  new_option_N_text_area_wrapper.setAttribute('class' , 'create-answer-N-container');
  new_option_N_text_area_wrapper.setAttribute('id', new_id_spliited_lastChildContainer);

  var new_checkbox_inner = document.createElement('div');
  new_checkbox_inner.setAttribute('class' , 'create-qeuestion-radio-inner-circle');
  //create Element for checkBox outerCircle
  var new_checkbox = document.createElement('div');
  new_checkbox.setAttribute('class' , 'create-qeuestion-radio-outer-circle');
  //append innerCircle
  new_checkbox.appendChild(new_checkbox_inner);

  var new_answer = document.createElement('textarea');
  new_answer.setAttribute('name' , 'textareaopt');
  new_answer.setAttribute('id' , 'opt'+ (parseInt(lastChildString) + 1));
  new_answer.setAttribute('class' , 'create-answer-textarea');
  new_answer.setAttribute('placeholder' , 'opt'+ (parseInt(lastChildString) + 1))
  new_answer.style.fontSize = "14pt";

  new_option_N_text_area_wrapper.appendChild(new_checkbox);
  new_option_N_text_area_wrapper.appendChild(new_answer);
  new_option_text_area_wrapper.appendChild(new_option_N_text_area_wrapper);
  new_div_fr_answer_container_id.appendChild(new_textarea_fr_create_question_textarea_id);
  new_div_fr_answer_container_id.appendChild(new_option_text_area_wrapper);
  all_question_asnwer_container.appendChild(new_div_fr_answer_container_id);
}

All of them are just the repetitive methods. So my question is, is there a more efficient way of doing this or is there like a class that I can use to make it more manageable.

Upvotes: 1

Views: 74

Answers (2)

Mister Jojo
Mister Jojo

Reputation: 22355

const 
  btAdd     = document.querySelector('button#bt-add')
, container = document.querySelector('div#container')
, builder = 
  { ref_id : 0
  , newDiv()
    {
    let sub_id = 0
      , div    = document.createElement('div')
      ;
    div.id  = ++this.ref_id
    div.innerHTML = `
    <div id="bla-bla-id_${this.ref_id}" class="xxzw">
      <em> text areas: </em><br>
      <textarea id="opt${this.ref_id}-${++sub_id}" placeholder="txt-area ${this.ref_id}-${sub_id} "></textarea><br>
      <textarea id="opt${this.ref_id}-${++sub_id}" placeholder="txt-area ${this.ref_id}-${sub_id} "></textarea>
    </div>`
    container.appendChild(div)
    }
  }
btAdd.onclick = e =>
  {
  builder.newDiv()
  }
div#container {
  margin  : 1em;
  width   : 20em;
  }
div#container > div {
  margin  : .8em 0;
  padding : .2em; 
  border  : 1px solid lightblue;
  }
em {
  font-size   : .7em;
  font-weight : bold;
  }
textarea {
  width : 90%;
  }
<button id="bt-add">add</button>
<div id="container"> </div>

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1075129

You have several options, two of which are:

  1. If you don't mind parsing HTML each time (and parsing HTML is really fast on modern browser), you can use insertAdjacentHTML instead, perhaps with a template literal so you can embed newlines easily and use ${x} syntax for embedded values:

    all_question_asnwer_container.insertAdjacentHTML("beforeend", `<div>
        (etc.)
    </div>`);
    
  2. You can use a template element in your HTML that you clone (via cloneNode) as needed. Here's the cloning part of MDN's example from the linked page

    var tbody = document.querySelector("tbody");
    var template = document.querySelector('#productrow'); // The template element
    
    // Clone the new row and insert it into the table
    var clone = template.content.cloneNode(true);
    var td = clone.querySelectorAll("td");
    td[0].textContent = "1235646565";
    td[1].textContent = "Stuff";
    
    tbody.appendChild(clone);
    

Upvotes: 1

Related Questions