Search code examples
javascripthtmlgoogle-sheetsappendclone

Record data to proper html children elements when cloning


I have a form in Google sheets where I send dynamic data from the sheet.

I loop sheet data and create same number of blocks in a form as number of my headers.

I have a first block of elements written in html. The rest of blocks I clone from the first one, clone its IDs and send my sheet titles to elements of the form.

First title I write (with the last title from an array) to the only block existing before looping.

Then I clone that block giving a clone new titles and trying to insert clones before the first block ([0]) but it gets wrong and overwrites my first hardcoded title.

It appends the blocks in random number, so I can't get my titles in the right sequence like "1,2,3". It's always like "2,1,1" or "1,3,2" etc.

I've tried different variations of appending the cloned elements (like motherDiv.appendChild(clonedRow) and motherDiv.insertBefore(clonedRow, motherDiv.children[0]), but cannot get it to work properly.

Please point to what's wrong here.

Here's the code and the screen:

//my google app backend function with data    
var numbers = [1, 2, 3]
var stats = ["Валовый доход", "Кол-во клиентов", "Кол-во размещенной рекламы"]

var stats = {

  statNumbers: numbers,
  statStats: stats
}



//select initialization
document.addEventListener('DOMContentLoaded', function() {
  var getstats = getStats();
  var elems = document.querySelectorAll('select');
  var instances = M.FormSelect.init(elems);
});


//getting stats object
function getStats() {

  google.script.run.withSuccessHandler(processStats).statsObj();
  google.script.run.withFailureHandler(showError).statsObj()

  return;
}


//creating dynamic stats fields in the form
function processStats(stats) {

  //name first statistic with the last number
  var firstStat = document.getElementById("statName").innerHTML = stats.statNumbers[stats.statStats.length - 1] + ". " + stats.statStats[stats.statStats.length - 1]

  //mother div
  var motherDiv = document.getElementById("motherDiv")

  //sample row to copy
  var statRow = document.getElementById("statsample");

  //loop stat names
  for (var i = 0; i < stats.statStats.length - 1; i++) {

    var statName = stats.statStats[i]
    var statNumber = stats.statNumbers[i]

    //cloning the original row
    var clonedRow = statRow.cloneNode(true)

    //setting unique ID to whole row
    var rowId = clonedRow.id = "statsample" + i

    //setting unique ID to stat div
    var clonedStatID = motherDiv.getElementsByClassName("statClass")[0].id = "statName" + statNumber

    //stat titles (except first one)
    var statHtml = document.getElementById(clonedStatID).innerHTML = statNumber + ". " + statName;

    var err = document.getElementById("err").innerHTML = motherDiv.children.length

    //appending it to the mother div
    //motherDiv.appendChild(clonedRow);
    motherDiv.insertBefore(clonedRow, motherDiv.children[0]);

  }
  return;
}



function showError() {

  var err = document.getElementById("err").innerHTML = "There was an error."
}
<!--Import Google Icon Font-->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">

<div id="motherDiv">
  <!-- mother div -->

  <div class="row" id="statsample">

    <div class=" input-field col s3 #fffde7 yellow lighten-5">
      <h6 id="statName" class="statClass"></h6>
    </div>

    <div class="input-field col s3">
      <select class="icons browser-default" id="weeksSel4">
        <option value="" disabled selected>Неделя</option>
      </select>
    </div>

    <div class="input-field col s2">
      <input id="numbers" type="text" class="validate">
      <label for="numbers">Значение</label>
    </div>

    <div class="input-field col s1.5">
      <select class="icons browser-default" id="measure">
        <option value="" disabled selected>Ед. изм:</option>
        <option value="">шт.</option>
        <option value="">%</option>
        <option value="">$</option>
        <option value="">руб.</option>
        <option value="">грн.</option>
      </select>
    </div>

    <div class="input-field col s2.5">
      <a class="waves-effect waves-light btn-small #039be5 light-blue darken-1" style="float: right;" id="btn1row"><i class="material-icons right">send</i>Ввести</a>
    </div>

  </div>
  <!-- row end -->
</div>
<!-- mother div end-->

<div id="err"> </div>

enter image description here


Solution

  • I'd recommend populating the 'master' div with the first element of the array and for the clones start iterating over the array from index 1. We need to make some fundamental changes to your processStats(stats) function. First populate the master:

      var firstStat = document.getElementById("statName").innerHTML = stats.statNumbers[0] + ". " + stats.statStats[0];
    

    the following two lines are untouched

      var motherDiv = document.getElementById("motherDiv")
      var statRow = document.getElementById("statsample");
    

    Change the for loop to start from 1

      for (var i = 1; i < stats.statStats.length; i++) {
      }
    

    Now the for-loop itself needs some changes. These three lines are okay

    var statName = stats.statStats[i];
    var statNumber = stats.statNumbers[i];
    var clonedRow = statRow.cloneNode(true);
    

    What I don't understand is the following

    var clonedStatID = motherDiv.getElementsByClassName("statClass")[0].id = "statName" + statNumber
    

    I know you want to give the freshly cloned divs child statClass a unique id with a sequential number but with every iteration of the for-loop this would give the first element in the list the id. Above you already have a variable which holds a reference to the cloned div clonedRow you can also use to access it's children.

    So get rid of the line and use this:

    clonedRow.getElementsByTagName("h6")[0].innerHTML = statNumber + ". " + statName;
    

    Now just append the div to the parent

    motherDiv.appendChild(clonedRow);
    

    Here's a working example - just click 'Run code snippet' to see it in action:

    //my google app backend function with data    
    var numbers = [1, 2, 3]
    var stats = ["Валовый доход", "Кол-во клиентов", "Кол-во размещенной рекламы"]
    
    var stats = {
    
      statNumbers: numbers,
      statStats: stats
    }
    
    function processStats(stats) {
    
      //name first statistic with the last number
      var firstStat = document.getElementById("statName").innerHTML = stats.statNumbers[0] + ". " + stats.statStats[0];
    
      //mother div
      var motherDiv = document.getElementById("motherDiv")
    
      //sample row to copy
      var statRow = document.getElementById("statsample");
    
      //loop stat names
      for (var i = 1; i < stats.statStats.length; i++) {
    
        var statName = stats.statStats[i];
        var statNumber = stats.statNumbers[i];
        var clonedRow = statRow.cloneNode(true);
        var rowId = clonedRow.id = "statsample" + i;
        clonedRow.getElementsByTagName("h6")[0].innerHTML = statNumber + ". " + statName;
        var err = document.getElementById("err").innerHTML = motherDiv.children.length
        motherDiv.appendChild(clonedRow);
      }
      return;
    }
    
    function showError() {
      var err = document.getElementById("err").innerHTML = "There was an error."
    }
    
    processStats(stats);
    <!--Import Google Icon Font-->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    
    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    
    <div id="motherDiv">
      <!-- mother div -->
    
      <div class="row" id="statsample">
    
        <div class=" input-field col s3 #fffde7 yellow lighten-5">
          <h6 id="statName" class="statClass"></h6>
        </div>
    
        <div class="input-field col s3">
          <select class="icons browser-default" id="weeksSel4">
            <option value="" disabled selected>Неделя</option>
          </select>
        </div>
    
        <div class="input-field col s2">
          <input id="numbers" type="text" class="validate">
          <label for="numbers">Значение</label>
        </div>
    
        <div class="input-field col s1.5">
          <select class="icons browser-default" id="measure">
            <option value="" disabled selected>Ед. изм:</option>
            <option value="">шт.</option>
            <option value="">%</option>
            <option value="">$</option>
            <option value="">руб.</option>
            <option value="">грн.</option>
          </select>
        </div>
    
        <div class="input-field col s2.5">
          <a class="waves-effect waves-light btn-small #039be5 light-blue darken-1" style="float: right;" id="btn1row"><i class="material-icons right">send</i>Ввести</a>
        </div>
    
      </div>
      <!-- row end -->
    </div>
    <!-- mother div end-->
    
    <div id="err"> </div>