Javascript for loop only creates one child - javascript

I have an array of js objects, and for every object in array I am trying to create a div with a template, designated with <template></template>. I am trying to do this using a for loop. The for loop does exactly what I want, one time.
Here is my HTML template to be copied:
<template id="tracktemp">
<div class="trackerItem">
<div class="itemNameTextCont">
<p class="itemNameText">Combatant Name</p>
</div>
<div class="itemHpTextCont">
<p class="itemHpText">HP 100</p>
</div>
<div class="itemApTextCont">
<p class="itemApText">AP 30</p>
</div>
<div class="itemActionButtonsCont">
<div class="actionButtonCont"><button class="actionButton">Heal</button></div>
<div class="actionButtonCont"><button class="actionButton">Dam</button></div>
<div class="actionButtonCont"><button class="actionButton">Stat</button></div>
<div class="actionButtonCont"><button class="actionButton">Abil</button></div>
</div>
<div class="itemModifyButtonsCont">
<div class="modifyButtonCont"><button class="modifyButton">Edit</button></div>
<div class="modifyButtonCont"><button class="modifyButton">Remove</button></div>
</div>
</div>
</template>
The HTML Section it's being copied into (within trackerCont)
<section class="trackerModule">
<div class="trackerModuleCont">
<div class="trackerHeaderCont">
<h2 class="trackerHeader">Combatants</h2>
</div>
<div class="trackerTopButtonsCont">
<div class="topButtonCont"><button class="topButton">Add</button></div>
<div class="topButtonCont"><button class="topButton">Add From</button></div>
</div>
<div id="trackerCont" class="trackerCont">
</div>
<div class="trackerBottomButtonsCont">
<div class="bottomButtonCont"></div>
<div class="bottomButtonCont"></div>
</div>
</div>
</section>
And the JS doing the copying
var trackerArray = [
{Name: "Joe Brown", HP: 100, AP: 30},
{Name: "Steve Smith", HP: 100, AP: 30},
{Name: "Jane", HP: 100, AP: 30}
]
function buildTracker() {
var trackerLength = trackerArray.length;
var trackItemTemp = document.getElementById('tracktemp').content.cloneNode(true);
for (i = 0; i < trackerLength; i++) {
var combatant = trackerArray[i];
var itemParent = document.getElementById('trackerCont');
trackItemTemp.querySelector('.itemNameText').innerText = combatant.Name;
trackItemTemp.querySelector('.itemHpText').innerText = combatant.HP;
trackItemTemp.querySelector('.itemApText').innerText = combatant.AP;
document.createElement(trackItemTemp);
}
}
I understand to some extent that appendChild can only create one element. I should be using document.createElement instead, no? I do not understand how to go about rewriting the code to copy the HTML template using createElement... If someone could lend me a hand that would be great! Thanks!

From
document.createElement(trackItemTemp);
To
document.getElementById("trackerCont").appendChild(trackItemTemp);
(edit)
I missed whole function code and i also missed there was a variable itemParent so I edited the last line of the function. Make sure to use cloneNode in each loop.
function buildTracker() {
var trackerLength = trackerArray.length;
var trackItem;
for (i = 0; i < trackerLength; i++) {
var trackItemTemp = document.getElementById('tracktemp').content.cloneNode(true);
var combatant = trackerArray[i];
var itemParent = document.getElementById('trackerCont');
trackItemTemp.querySelector('.itemNameText').innerText = combatant.Name;
trackItemTemp.querySelector('.itemHpText').innerText = combatant.HP;
trackItemTemp.querySelector('.itemApText').innerText = combatant.AP;
itemParent.appendChild(trackItemTemp);
}
}

Related

slice an array based on indeces

I have the current HTML:
<div id="1-1">TITLE1</div>
<div id="1-2">TITLE2</div>
<div id="1-3">TITLE3</div>
<div id="1-4">TITLE4</div>
<div id="1-5">TITLE5</div>
<div class="topic">A1</div>
<div class="topic">B2</div>
<div class="topic">C3</div>
<div class="topic">D4</div>
<div class="topic">E5</div>
<div class="topic">F6</div>
<div class="topic">G7</div>
<div class="topic">H8</div>
<div class="topic">I9</div>
<div class="topic">J10</div>
<div class="topic">K11</div>
<div class="topic">L12</div>
<div class="topic">M13</div>
<div class="topic">N14</div>
<div class="topic">O15</div>
And I want to use JavaScript to create a for loop that will append the correct .topic nodes into the correct TITLE div based on this object:
all = {'1-1': ['A', 'B', 'C'],
'1-2': ['D'],
'1-3': ['E', 'F', 'G', 'H'],
'1-4': ['I', 'J'],
'1-5': ['K', 'L', 'M', 'N', 'O']
};
Where the loop will ideally result in:
<div id="1-1">TITLE1
<div class="topic">A1</div>
<div class="topic">B2</div>
<div class="topic">C3</div>
</div>
<div id="1-2">TITLE2
<div class="topic">D4</div>
</div>
<div id="1-3">TITLE3
<div class="topic">E5</div>
<div class="topic">F6</div>
<div class="topic">G7</div>
<div class="topic">H8</div>
</div>
<div id="1-4">TITLE4
<div class="topic">I9</div>
<div class="topic">J10</div>
</div>
<div id="1-5">TITLE5
<div class="topic">K11</div>
<div class="topic">L12</div>
<div class="topic">M13</div>
<div class="topic">N14</div>
<div class="topic">O15</div>
</div>
I've taken a stab at this by creating two arrays -- one of the "TITLE*" locations in all and another of how many "topics" should be in each slice
let header_position = [0,4,6,11,14]
let topics_between = [3,1,4,2,5]
I was trying to leverage this and find a pattern for the for loop so that I can use these array numbers in order to put things in the right place but I can't seem to figure out the proper loop. Any help would be appreciated:
Attempted Solution
for (i = 0; i < header_position.length; i++) {
// i = 0 topics.slice(0,2)
// i = 2 topics.slice(3,3)
// i = 3 topics.slice(4,7)
// i = 4 topics.slice(8,9)
// i = 5 topics.slice(10,14)
let currentslice = topics.slice(header_position[i] + 1, header_position[i + 1] - 2)
if (i === 0) {
currentslice = topics.slice(0, topics_between[i] - 1)
}
console.log(currentslice)
currentslice.appendTo("#1-" + [i + 1])
}
This loop does not work as it stands, but I would appreciate any help or considerations in trying to get this logic functional!
Your question seems pretty theorical... And looks like an XY problem to me.
I assume your titles certainly will not be TITLE1, TITLE2 and so on with some real content.
I suppose you managed to define the all array to have a start of organisation of your page... But from what's posted, I can't suggest any better way.
So if I just take the all and the header_position arrays as-is, the loop would use two indexes, one for the headers and one for the topics.
Have a look below and tell me if that answers your theorical question as well as your concrete need.
let all = [ "TITLE1", "A", "B", "C",
"TITLE2", "D",
"TITLE3", "E", "F", "G", "H",
"TITLE4", "I", "J",
"TITLE5", "K", "L", "M", "N", "O"]
let header_position = [0,4,6,11,14]
//let topics_between = [3,1,4,2,5] // not used
let headerIndex = -1
let topicIndex = 0
for(i=0;i<all.length;i++){
if(header_position.indexOf(i)>-1){
headerIndex++
continue
}
$(".header").eq(headerIndex).append($(".topic").eq(topicIndex))
topicIndex++
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header" id="1-1">TITLE1</div>
<div class="header" id="1-2">TITLE2</div>
<div class="header" id="1-3">TITLE3</div>
<div class="header" id="1-4">TITLE4</div>
<div class="header" id="1-5">TITLE5</div>
<div class="topic">A1</div>
<div class="topic">B2</div>
<div class="topic">C3</div>
<div class="topic">D4</div>
<div class="topic">E5</div>
<div class="topic">F6</div>
<div class="topic">G7</div>
<div class="topic">H8</div>
<div class="topic">I9</div>
<div class="topic">J10</div>
<div class="topic">K11</div>
<div class="topic">L12</div>
<div class="topic">M13</div>
<div class="topic">N14</div>
<div class="topic">O15</div>
From what I have understood, you want it to be split up by title. You can do this:
let count = 0;
for (let i = 0; i < all.length; i++) {
if (all[i].includes("TITLE")) {
let element = document.createElement("div");
element.innerHTML = all[i];
element.id = `1-${count + 1}`;
document.body.appendChild(element);
count++;
} else {
let element = document.createElement("div");
element.className = "topic";
element.innerHTML = `${all[i]}${i - count + 1}`;
document.getElementById(`1-${count}`).appendChild(element);
}
}
Let me know if it works.

Add an existing div element with classes (along with its underlying elements) to a div

I need to have a function that would add an existing div with a class (along with its underlying elements) to a particular div using for loop. It looks like this:
<div class="left-col">
<div class="list-row">
<div class="list-row2">
<span>Hello</span>
</div>
</div>
</div>
I need to loop through a function that will produce or duplicate "list-row" twice.
$(function() {
var leftcol = document.getElementsByClassName('left-col');
for (var i = 0; i < 2; i++) {
var listrow = document.querySelector('.list-row');
leftcol.appendChild(listrow[i]);
}
})
It should look like this:
<div class="left-col">
<div class="list-row">
<div class="list-row2">
<span>Hello</span>
</div>
</div>
<div class="list-row">
<div class="list-row2">
<span>Hello</span>
</div>
</div>
<div class="list-row">
<div class="list-row2">
<span>Hello</span>
</div>
</div>
</div>
You can try the following way:
$(function() {
var leftcol = document.querySelector('.left-col');
for (let i = 0; i < 2; i++) {
var listrow = document.querySelector('.list-row').cloneNode();
listrow.textContent = i + 1 + listrow.textContent;
leftcol.appendChild(listrow);
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="left-col">
<div class="list-row">0</div>
</div>
You could use cloneNode and set the deep property to true. This will clone the node and all of its descendants.
For example:
function cloneNode(copies = 1) {
for (let i = 0; i < copies; i++) {
let leftcol = document.getElementsByClassName('left-col')[0];
let nodeToClone = document.querySelector(".list-row");
let clonedNode = nodeToClone.cloneNode(true);
leftcol.appendChild(clonedNode);
}
}
clone.addEventListener("click", function() {
cloneNode();
});
<button id="clone" type="button">Clone Node</button>
<div class="left-col">
<div class="list-row">Test</div>
</div>
If you wanted to insert more than one copy, you could pass a different value to the cloneNode function.
You can use jQuery's .clone() method to copy the entire content of an element to another element. The boolean argument passed to the clone function determines whether the events associated with the cloned element has to be copied or not. true indicates all the events associated with that div has to be copied.
$(function() {
$('.list-row').each(function(){
$(".left-col").append($(this).clone(true));
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div class="left-col">
<div class="list-row"><h1>This is original row</h1></div>
</div>
$(function() {
var leftcol = document.getElementsByClassName('left-col');
var listrow = document.querySelector('.list-row');
for (var i = 0; i < 2; i++) {
leftcol.appendChild(listrow.clone(true));
}
})

How to loop through JavaScript object in HTML?

I want to loop through a JavaScript object and repeat an html script as many times as the object length.
Here, I have the following in a script tag
<script>
var obj;
ipcRenderer.on('requests-results', (event, hosSchema) => {
obj = hosSchema
})
</script>
obj is an array retrieved from Mongo database as the picture below shows:
and I have the following inside <body> tag:
<div class="row">
<div class="col-md-4 col-sm-4">
<div class="card">
<div class="card-content">
<span class="card-title">.1.</span>
<p>.2.</p>
</div>
<div class="card-action">
.3.
.4.
</div>
</div>
</div>
</div>
How can I loop through obj to repeat the code between <div> tag as many times as obj.length?
I would suggest you to use Handlebars as #Amit mentioned.
first move out the code inside <div id="page-inner"> as below:
<div id="page-inner">
</div>
<script id= "requests-template" type="text/x-handlebars-template">
<div class="row">
{{#each requests}}
<div class="col-md-4 col-sm-4">
<div class="card">
<div class="card-content">
<span class="card-title">{{this.fieldName}}</span>
<p>{{this.fieldName}}</p>
</div>
<div class="card-action">
{{this.fieldName}}
{{this.fieldName}}
</div>
</div>
</div>
{{/each}}
</div>
</script>
Then inside another script page of type text/javascript you create the requests and assigned obj/hosSchema to it as below:
<script type="text/javascript">
var requestInfo = document.getElementById('requests-template').innerHTML;
var template = Handlebars.compile(requestInfo);
var requestData = template({
requests: obj
})
$('#page-inner').html(requestData);
</script>
NOTE: you need handlebars package installed (npm install handlebars --save)
Use templating script like Handlebars.js, Mustache.js or underscore.js.
Check below link for more description.
http://www.creativebloq.com/web-design/templating-engines-9134396
Try this:
var divlist = document.getElementsByTagName['div'];
var duplicate = null;
var rowIndex = -1;
var found = false;
for(var i = 0;i<obj.length;i++)
{
if(!found)
for(var p = 0;p<divlist.length;p++)
{
if(rowIndex != -1 && duplicate != null)
{
//set a Boolean to true and break
found = true;
break;
}
if(divlist[p].className == "col-md-4 col-sm-4")
{
//copy the element
duplicate = divlist[p];
}
else if(divlist[p].className == "row")
{
//identify the row's index
rowIndex = p;
}
}
//append the duplicate
divlist[rowIndex].appendChild(duplicate);
}

Change index (order) of elements via plain JS

Today I have a question: How to change index of elements via plain JS?
I have this example code:
<div class="foo">
<div class="f6x1"></div>
<div class="f6x2"></div>
<div class="f6x3"></div>
</div>
So I want to change order of (for example) first element, like this:
<div class="foo">
<div class="f6x2"></div>
<div class="f6x1"></div>
<div class="f6x3"></div>
</div>
Is there any "non-laging" example how to do that?
You need to select element and the move it before the next element after the second:
var first = document.querySelector('.f6x1'),
next = first.nextElementSibling;
first.parentNode.insertBefore(first, next.nextElementSibling);
<div class="foo">
<div class="f6x1">1</div>
<div class="f6x2">2</div>
<div class="f6x3">3</div>
</div>
Reference:
Element.querySelector
Node.nextElementSibling
Node.insertBefore
A more generic way to order the elements in any order you need:
first, start indexing using data-*-attributes:
<div class="foo">
<div data-index="1" class="f6x1"></div>
<div data-index="2" class="f6x2"></div>
<div data-index="3" class="f6x3"></div>
</div>
Second, use a function to reorder, something like:
var sortElement = document.querySelector('[data-sortable]');
document.querySelector('#buttonblock').addEventListener('click', doSort);
function doSort(e) {
var origin = e.target || e.srcElement;
if (/button/i.test(origin.nodeName)) {
var sortorder = origin.getAttribute('data-order').split(',');
orderElements(sortElement, sortorder);
}
return true;
}
function orderElements(elementRoot, order) {
var placeholder = document.createElement('div');
while(order.length) {
var current = order.shift();
var element = elementRoot.querySelector('[data-index="'+current+'"]');
if (element) {
placeholder.appendChild(element);
}
}
elementRoot.innerHTML = '';
elementRoot.innerHTML = placeholder.innerHTML;
}
<div data-sortable="true" class="foo">
<div data-index="1" class="f6x1">f6x1</div>
<div data-index="2" class="f6x2">f6x2</div>
<div data-index="3" class="f6x3">f6x3</div>
</div>
<div id="buttonblock">
<button data-order="2,1,3">sort 2, 1, 3</button>
<button data-order="3,2,1">sort 3, 2, 1</button>
<button data-order="3,1,2">sort 3, 1, 2</button>
</div>

JQuery Sort Divs by child divs

I have the following list of divs and I'd like to be able to sort them using Javascript / JQuery.
<div class="item">
<div class="genre">Classical</div>
<div class="name">Alpha</div>
<div class="location">London</div>
</div>
<div class="item">
<div class="genre">Blues</div>
<div class="name">Bravo</div>
<div class="location">New York</div>
</div>
<div class="item">
<div class="genre">Pop</div>
<div class="name">Charlie</div>
<div class="location">Paris</div>
</div>
<div class="buttons">
Sort by Genre
Sort by Name
Sort by Location
</div>
I'd like to be able to sort the items by their Genre/Name/Location alphabetically.
Example: If Sort by Genre was clicked, it would sort the items in 0-9 A-Z by Genre.
If any of you have any tips it would greatly be appreciated.
Cheers :)
You have to make a little change to html like following:
<div id="container">
<div class="item">
<div class="genre">Classical</div>
<div class="name">Alpha</div>
<div class="location">London</div>
</div>
<div class="item">
<div class="genre">Blues</div>
<div class="name">Bravo</div>
<div class="location">New York</div>
</div>
<div class="item">
<div class="genre">Pop</div>
<div class="name">Charlie</div>
<div class="location">Paris</div>
</div>
</div>
<div class="buttons">
Sort by Genre
Sort by Name
Sort by Location
</div>
jQuery
function sorting(tag) {
var items = $('div.item').sort(function(a, b) {
var txt1 = $.trim($('div.' + tag, a).text()),
txt2 = $.trim($('div.' + tag, b).text());
if (txt1 > txt2) return 1;
else return -1;
});
return items;
}
$('.buttons a').on('click', function(e) {
e.preventDefault();
$('div#container').html(sorting(this.id));
});
Working Sample
Ok, this would be my pure JS solution.
First, we should wrap your <div>s into a larger container.
<div id = "wrapper">
<div id = "item">...</div>
<div id = "item">...</div>
<div id = "item">...</div>
</div>
Now, let's define a constant - which property do you want to sort it by? (this will probably be a function parameter later in your code).
var propName = "genre";
Let's get all the <div>s and put them in an array.
var items = document.getElementsByClassName("item");
var itemsArray = new Array();
Let us sort them lexicographically according to the text of the selected property.
for (var i = 0; i < items.length; i++)
itemsArray.push(items[i]);
itemsArray.sort(function(a, b) {
var aProp = a.getElementsByClassName(propName)[0].firstChild.nodeValue;
var bProp = b.getElementsByClassName(propName)[0] .firstChild.nodeValue;
if (aProp < bProp)
return -1;
else if (aProp > bProp)
return 1;
else
return 0;
});
Let us construct a document fragment consisting of the sorted <div>s.
var fragment = document.createDocumentFragment();
for (var i = 0; i < itemsArray.length; i++)
fragment.appendChild(itemsArray[i].clone());
Finally, let us clear the contents of the <div id = "wrapper"> and replace it with the document fragment.
document.getElementById('wrapper').innerHTML = '';
document.getElementById('wrapper').appendChild(fragment);
Also, note that document.getElementsByClassName does not work in IE<9, but I was now really lazy to cope with that issue.
A fiddle: http://jsfiddle.net/nNXr4/
Check this beast:
function sortByCreatedOnAsc(a,b){
return $(a).find('.created_on').text() > $(b).find('.created_on').text();
}
function sortByCreatedOnDesc(a,b){
return $(a).find('.created_on').text() < $(b).find('.created_on').text();
}
function reorderEl(el){
var container = $('#tasks');
container.html('');
el.each(function(){
$(this).appendTo(container);
});
}
$('#created_on').click(function(){
if($(this).hasClass("asc")){
reorderEl($('.task').sort(sortByCreatedOnDesc));
$(this).removeClass("asc");
$(this).addClass("desc");
} else {
reorderEl($('.task').sort(sortByCreatedOnAsc));
$(this).removeClass("desc");
$(this).addClass("asc");
}
return false;
});
jsfiddle: http://jsfiddle.net/jKJc3/116/

Categories