0

I want to create 100 elements inside div 'view', all with the same UI template as defined inside div 'personViewPane-template'.

I also have an array of 100 objects of class 'personViewPane' in js and each object is bound to its corresponding DOM element which was previously created so that I can manipulate each DOM element using the corresponding object's functions.

HTML

<head>
<style>
*{border:0;margin:0;padding:0;box-sizing:border-box;font-family:Calibri;}
.container{display:inline-block;height:170px;padding:5px;background:rgb(0,150,200);margin:5px;}
.image{width:140px;height:140px;background-size:cover;}
.text{width:140px;height:20px;text-align:center;color:white;}
</style>
</head>


<body>

<div id="view"></div>

<!--//////////////templates//////////////-->
<div style="display:none;">

<div id="personViewPane-template" >
<div id="{{componentID}}" class="container">
    <div class="image" style="background:red;"></div>
    <div class="text"></div>
</div>
</div>

</div>
<!--//////////end of templates////////////-->

</body>

Here is the flow

Inside loop

  • Create an object of personViewPane by calling constructor personViewPane(id,id_of_parent_Element)

Inside constructor

  • Load the html template into object by calling loadTemplate()
  • In the template, replace the field '{{componentID}}' with dynamically created id by calling render()
  • Add the template to the div 'view'
  • Get the newly added DOM element by calling createHandle() and store this in variable handle
  • Add event listeners to the DOM element by calling addListeners()
  • Add the variables to the DOM element by calling validateUI()

The problem is that addEventListener works only on last created element. I found similar questions such as this one where addEventListener is called inside loop but I want to have it invoked from the constructor.

Any ideas?

JavaScript

<script>

class pane
{
componentID="";
template="";
handle;

constructor()   
{this.componentID="";}

loadTemplate(x) 
{this.template=x;}

render()    
{this.template = this.template.replace('{{componentID}}',this.componentID);return this.template;}

createHandle()  
{this.handle=document.getElementById(this.componentID);}

getHandle() 
{return this.handle;}

}//end of class pane




class personViewPane extends pane
{
name="";

constructor(id, container)
{
    super();
    this.componentID=personViewPane.name+id;
    this.name = "user"+id;
    this.loadTemplate(document.getElementById('personViewPane-template').innerHTML);    
    document.getElementById(container).innerHTML+=this.render();
    this.createHandle();
    this.addListeners();
    this.validateUI();
}

validateUI(){this.getHandle().getElementsByClassName('text')[0].innerHTML= this.name;}

addListeners()
{
    var parent = this;
    var showName = function(){alert(parent.componentID);}
    this.getHandle().addEventListener( "click", showName);
}


}//end of class personViewPane



var p =[];

for(let i=0; i<100; i++)
{
p[i] = new personViewPane(i,'view');  // create objects
}

</script>
JPK
  • 95
  • 1
  • 8

1 Answers1

2

The problem with this code is

document.getElementById(container).innerHTML+=this.render();

It is actually replacing the old templates which has click listener with just templates. I used appendChild instead of innerHTML and it worked perfectly. As collateral effect I have changed loadTemplate, render and the constructor function.

loadTemplate(x) {
  this.template = x.cloneNode(true);
}

render() {
  this.template.setAttribute('id', this.componentID);
  return this.template;
}

constructor(id, container) {
  super();
  this.componentID = personViewPane.name + id;
  this.name = "user" + id;
  this.loadTemplate(
    document.getElementById("personViewPane-template").firstElementChild
  );
  document.getElementById(container).appendChild(this.render());
  this.createHandle();
  this.addListeners();
  this.validateUI();
}

I tried out the same in code pen. Here is the link to it.

Dixy Xavier
  • 667
  • 2
  • 6
  • 20