Group 'for loop' output into groups of four - javascript

I have a JS object array that I am using a for loop to print to the page:
// For loop through druid specs
for (let i = 0; i < druidSpecs.length; i++) {
// Get spec
let spec = druidSpecs[i];
// Log Spec name, background, talents
console.log(spec.name, spec.background);
// Create spec containers
html += `
<div id="${spec.name.replace(/\s+/g, '-').toLowerCase()}" class="container">
`;
// Loop through spec talent objects
for (let i = 0; i < spec.talents.length; i++) {
let talent = spec.talents[i];
// If empty string, add empty talent box, else, add real talent
if (talent.name == "") {
html += `
<div class="blank talent"></div>
`;
} else {
html += `
<div id="${talent.name.replace(/\s+/g, '-').toLowerCase()}" class="talent" style="background: url('${talent.icon}')">
<h2 class="talent-name">${talent.name}</h2>
<h3 class="description">${talent.description}</h3>
</div>
`;
}
}
// Close spec containers
html += `
</div>
`;
}
If you're familiar with World of Warcraft talents then this may make more sense. The first loop creates a container div for each class 'spec'. Then each spec has 16 talent spaces that are grouped in rows of four.
As you can see I can get the talents to print in one large list within the spec container, but I have no clue how to wrap every four talent spaces within a container div.
Ultimately, the final html output would be something like:
<div class="container" id="balance">
<div class="tier">
<div class="talent" id="improved-wrath" style="background: url('src/assets/druid/icons/0.png')">
<h2 class="talent-name">Improved Wrath</h2>
<h3 class="description">Reduces the cast time of your Wrath spell by ${value1} seconds.</h3>
</div>
<div class="talent" id="nature's-grasp" style="background: url('src/assets/druid/icons/1.png')">
<h2 class="talent-name">Nature's Grasp</h2>
<h3 class="description">While active, any time an enemy strikes the caster they have a ${value1}% chance to become afflicted by Entangling Roots (Rank 1). Only useable outdoors. 1 charge. Lasts 45 sec.</h3>
</div>
<div class="talent" id="improved-nature's-grasp" style="background: url('src/assets/druid/icons/1.png')">
<h2 class="talent-name">Improved Nature's Grasp</h2>
<h3 class="description">Increases the chance for your Nature's Grasp to entangle an enemy by ${value1}%.</h3>
</div>
<div class="blank talent"></div>
</div><!-- End tier 1 -->
<div class="tier">
<div class="talent" id="improved-entangling-roots" style="background: url('src/assets/druid/icons/2.png')">
<h2 class="talent-name">Improved Entangling Roots</h2>
<h3 class="description">Gives you a ${value1}% chance to avoid interruption caused by damage while casting Entangling Roots.</h3>
</div>
<div class="talent" id="improved-moonfire" style="background: url('src/assets/druid/icons/3.png')">
<h2 class="talent-name">Improved Moonfire</h2>
<h3 class="description">Increases the damage and critical strike chance of your Moonfire spell by ${value1}%.</h3>
</div>
<div class="talent" id="natural-weapons" style="background: url('src/assets/druid/icons/4.png')">
<h2 class="talent-name">Natural Weapons</h2>
<h3 class="description">Increases the damage you deal with physical attacks in all forms by ${value1}%.</h3>
</div>
<div class="talent" id="natural-shapeshifter" style="background: url('src/assets/druid/icons/5.png')">
<h2 class="talent-name">Natural Shapeshifter</h2>
<h3 class="description">Reduces the mana cost of all shapeshifting by ${value1}%.</h3>
</div>
</div><!-- End tier 2 -->
etc..
</div>
Thanks for any help!

One approach is use a for loop that increments by 4 and use Array#slice() within that loop to get each group of 4 items.
This allows you to create a wrapping element within each iteration of the for loop and iterate the sub group of items to put in that wrapping element
Basic example:
const data = Array(10).fill(0).map((_, i) => `Item #${i+1}`);
const container = document.getElementById('cont')
for (let i = 0; i < data.length; i = i + 4) {
const group = document.createElement('div')
group.className = 'group';
const items = data.slice(i, i + 4);
items.forEach(item => {
const itemDiv = document.createElement('div')
itemDiv.textContent = item
group.append(itemDiv)
})
container.append(group)
}
.group {
border: 2px solid #ccc;
margin: .5em
}
<div id="cont"></div>

You can create a counter with the value of iterator variable (i) from the for loop which is going through Talents. I've modified your code to depict what I mean. You might have to slightly modify it as per your requirements. Hope it helps. Also, I moved the code appending the HTML to a function because we will now need to call it at 2 places.
EDIT: I've modified the code, instead of using i for the comparision, I've added a separate counter. And this time instead of checking for modulus, I'm straight up checking if the value of counter has been incremented to 4, when it is 4, I'm running the else condition and setting the counter back to 0. I think this will do the trick.
import _ from 'lodash';
import classData from '.\\assets\\classData.js';
import './style.css';
var druidSpecs = classData.druid.specs;
let html = '';
// For loop through druid specs
for (let i = 0; i < druidSpecs.length; i++) {
// Get spec
let spec = druidSpecs[i];
// Create spec containers
html += `
<div id="${spec.name.replace(/\s+/g, '-').toLowerCase()}" class="container">
`;
const buildTalentHTML = talent => {
// If empty string, add empty talent box, else, add real talent
if (talent.name) {
//empty string is a falsy value, you can skip the equality operator
html += `
<div class="blank talent"></div>
`;
} else {
// If empty string, add empty talent box, else, add real talent
html += `
<div id="${talent.name
.replace(/\s+/g, '-')
.toLowerCase()}" class="talent" style="background: url('${
talent.icon
}')">
<h2 class="talent-name">${talent.name}</h2>
<h3 class="description">${talent.description}</h3>
</div>
`;
}
};
let talentCounter = 0;
// Loop through spec talent objects
for (let i = 0; i < spec.talents.length; i++) {
let talent = spec.talents[i];
if (talentCounter !== 4) {
buildTalentHTML(talent);
talentCounter++;
} else {
html += '<div class="tier">';
buildTalentHTML(talent);
if (talentCounter === 4) {
html += '</div>';
talentCounter = 0;
}
}
}
// Close spec containers
html += `
</div>
`;
}
document.body.insertAdjacentHTML('beforeend', html);

Related

Regex loop and number phone

Here is my code but I can't display the other numbers because I have indexed [0] and I don't know how I can display the other numbers.
Example string: "Hello, you can contact me at 0744224422 or 0192234422."
Result code : "Hello, you can contact me at <span>0744224422</span> or <span>0744224422</span>."
On this example: my code will replace "0192234422" by 0744224422 "which is logical" but I would like it to display 0192234422... How can I do it ?
Thanks
let selector = document.querySelectorAll('.message > div > .chat');
for (let index = 0; index < selector.length; index++) {
if (selector[index].innerText) {
let text = selector[index].innerText;
const regex = /(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}/gim;
if (text.match(regex).length) {
const newTexte = ` <span>${text.match(regex)[0].trim()}</span> `;
selector[index].innerHTML = text.replace(regex, newTexte);
};
}
}
If you use the $ replacement character of the replace function, it will put the right text in there. Rather than trim just put parentheses around the non-whitespace portion of your regular expression and effectively let the capturing group become the trim operation.
let selector = document.querySelectorAll('.message > div > .chat');
for (let index = 0; index < selector.length; index++) {
if (selector[index].innerText) {
let text = selector[index].innerText;
const regex = /(\d[\s-]?)?([\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4})/gim;
if (text.match(regex).length) {
const newTexte = ` <span class="red">$2</span> `;
selector[index].innerHTML = text.replace(regex, newTexte);
};
}
}
.red {
background: yellow
}
<div class="message">
<div>
<div class="chat">Hello, you can contact me at 0744224422 or 0192234422.</div>
</div>
</div>
I'm going to try to call attention to the difference in the regular expressions below: (because I added one set of parentheses)
/(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}/gim
( )
/(\d[\s-]?)?([\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4})/gim;
Do you have two separate instances of the selector? If not then the selector.length is only 1 which is why only the first number is shown. You can edit the html to have more than one instance of the selector (and style with display: inline so that it doesn't line break onto a new line) EX:
let selector = document.querySelectorAll('.message > div > .chat');
for (let index = 0; index < selector.length; index++) {
if (selector[index].innerText) {
let text = selector[index].innerText;
const regex = /(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}/gim;
if (text.match(regex).length) {
const newTexte = ` <span>${text.match(regex)[0].trim()}</span> `;
selector[index].innerHTML = text.replace(regex, newTexte);
};
}
}
<div class="message">
<div>
<p class="chat" style="display:inline">
Hello, you can contact me at 0744224422 or </p>
<p class="chat" style="display:inline">0192234422</p>
<!-- add more numbers as needed in another <p class="chat" style="display:inline" ></p>-->
</div>
</div>
Thank you for your answers, but I would just like to add a <span></span> (or more) when a phone number is written in the string..

Javascript inserting html code without closing tags

I have a list of table columns. I would like to display them in one row.
What am I trying is :
for (var i = 0; i < key.length; i++) {
writeToScreen3('<div class="col-sm">' + key[i] + '</div>'); //column name
}
function writeToScreen3(message) {
var pre = document.createElement("p"); //I realize I am creating another element <p> How to do it diffrently?
pre.innerHTML = message;
output.appendChild(pre);
}
What I need is this transferred to JavaScript :
<div class="container">
<div class="row">
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
</div>
</div>
What I also tried :
function test8() {
$("#output").html('<div class="container">< div class= "row" >'); //but it always closes these 2 divs here . I want it not to close it. output is a div field
}
You can do something like this:
function createContainer(columns) {
function createDivWithClass(cls) {
const div = document.createElement('div');
div.classList.add(cls);
return div;
}
const container = createDivWithClass('container');
const row = createDivWithClass('row');
container.appendChild(row);
for (let i = 0; i < columns.length; i++) {
const column = createDivWithClass('col-sm');
column.textContent = columns[i];
row.appendChild(column);
}
return container;
}
const container = createContainer([1, 2, 3, 4]);
document.getElementById('output').appendChild(container);
console.log(container.outerHTML);
<div id="output"></div>
Here, I've defined a function called createDivWithClass to make it easier to create a <div> and set a class name to it.
Then, I'm using this function to create a <div class="container">, create a <div class="row"> and add that to the container, and then go through the columns array and create <div class="col-sm"> and add those to the row element.
Just like you can append elements to the #output element in the DOM, you can also append elements to elements that you've created and are not yet in the DOM.

How do I sort items by array?

I understand that the wording of the question is incorrect (if someone can write it correctly, please). The task is this, I have 30 elements on the page and I need to sort them with the resulting array. That is, I get an array - let order = [2, 5, 3, 6, 12 ...] and sorting should take place in accordance with this order, that is, the first element is the 2nd element from HTML, the second element is the 5th element from HTML (according to the given array). The initial order is equal to the number in data-custom-sort.
There will be many such an array. And I don't understand how to do it universally. Can someone have any ideas?
I have not formulated very well, so if you have questions - ask.
The HTML is something like this:
<a id="sort-best" class="choose-cat">best</a>
<div>
<article data-custom-sort="1">
...
</article>
<article data-custom-sort="2">
...
</article>
<article data-custom-sort="3">
...
</article>
//and etc
</div>
These are product cards in the catalog. I need to sort them
document.querySelector('#sort-best').onclick = sortBest;
function sortBest() {
let nav = document.querySelector('#game-items-cart');
for (let i = 0; i < nav.children.length; i++) {
for (let j = i; j < nav.children.length; j++) {
if (+nav.children[i].getAttribute('data-sort') > +nav.children[j].getAttribute('data-sort')) {
replaceNode = nav.replaceChild(nav.children[j], nav.children[i]);
insertAfter(replaceNode, nav.children[i]);
}
}
}
}
function insertAfter(elem, refElem) {
return refElem.parentNode.insertBefore(elem, refElem.nextSibling);
}
I used this code to sort through the data attributes. That is, the number in the data attribute = the ordinal after sorting.
Like this?
let order = [2, 1, 3];
const container = document.getElementById("container");
document.getElementById("sort-best").addEventListener("click", e => {
e.preventDefault()
order.forEach(idx => container.appendChild(container.querySelector("[data-custom-sort='" + idx + "']")))
})
<a id="sort-best" class="choose-cat">best</a>
<div id="container">
<article data-custom-sort="1">
One
</article>
<article data-custom-sort="2">
Two
</article>
<article data-custom-sort="3">
Three
</article>
</div>
More generic:
const sortArticles = (cont, order) => {
const container = document.getElementById(cont);
order.forEach(idx => container.appendChild(container.querySelector("[data-custom-sort='" + idx + "']")))
};
document.getElementById("sort").addEventListener("click", e => {
const tgt = e.target;
if (tgt.classList.contains("choose-cat")) {
e.preventDefault()
sortArticles("container", tgt.dataset.order.split(","))
}
})
<div id="sort">
<a id="sort-best" class="choose-cat" data-order="3,1,2">best</a> |
<a id="sort-default" class="choose-cat" data-order="1,2,3">default</a>
</div>
<div id="container">
<article data-custom-sort="1">
One
</article>
<article data-custom-sort="2">
Two
</article>
<article data-custom-sort="3">
Three
</article>
</div>
Here is another way of doing this
// Sort reference array
const sortRef = [2, 5, 3, 1, 4];
// Get, sort, update function
const sortFn = () => {
// Apply new order by sorting and replacing sortContainer content
const newArray = [];
for(let i = 0; i < sortRef.length; i++) newArray.push(document.querySelector("[data-custom-sort='" + sortRef[i] + "']").outerHTML);
// Update html
document.getElementById("sortContainer").innerHTML = newArray.join('');
}
// Add click event
document.getElementById("clickMe").addEventListener('click', event => {
sortFn();
});
article {
border: 1px solid #ff0000;
padding: 3px;
width: 100px;
}
<div id="sortContainer">
<article data-custom-sort="1">
1
</article>
<article data-custom-sort="2">
2
</article>
<article data-custom-sort="3">
3
</article>
<article data-custom-sort="4">
4
</article>
<article data-custom-sort="5">
5
</article>
</div>
<p></p>
<button id="clickMe">Sort html data</button>

First and second name initials, preserving full surname

It's my first touch with javascript and I really need help. I have an html with divs containing from one to three "author" text values each. I need to make automate shorting the names (only first names) if there is more than one "author" in div.
Eg.
<div class="book">
<a> <h2>Book 1</h2>
<h3 class="author">Minty Estelle</h3>
<h3 class="author">Katey Josepha Shevon</h3></a></div>
<div class="book">
<a> <h2>Book 2</h2>
<h3 class="author">Leila Seward</h3></a></div>
So that output would be:
Book 1
M. Estelle
K.J. Shevon
Book 2
Leila Seward
So if there is only one "author" - his name stays unchanged. But if it's more than one - firstname and secondname (but not surname - last value) is shortened to first leter and followed by dot.
I searched a lor and played with compilations... but nothing worked. Is anyone can help to find a solution?
My js so far is:
var authorName = $(".book h3");
authorName.each(function(){
if(authorName.length > 1 && authorName.hasClass("author")){
var names = authorName.split(" ");
var shortened = names.not(:lastChild).map(s => s.slice(0, 1).append(". "));
document.authorName.innerHTML = shortened;
}
You'll have to loop over your books, then loop over every author of said book :
//For each book
$('.book').each(function() {
//If the book has more than one author
if ($('h3.author', this).length > 1) {
//For each author
$('h3.author', this).each(function() {
//Store the author name before emptying it
var words = $(this).text().split(' ');
$(this).text('');
//For each word inside the author name
for (var i = 0; i < words.length; i++) {
//If it's not the last name
if (i < words.length - 1) {
//Only keep the first letter
$(this).text($(this).text() + words[i].substring(0, 1) + '. ');
//Else keep the whole word
} else $(this).text($(this).text() + words[i]);
}
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="book">
<a>
<h2>Book 1</h2>
<h3 class="author">Minty Estelle</h3>
<h3 class="author">Katey Josepha Shevon</h3>
</a>
</div>
<div class="book">
<a>
<h2>Book 2</h2>
<h3 class="author">Leila Seward</h3>
</a>
</div>
This is a simple version, Adjust it for your own needs, and maybe refactor it to be more in functional way :)
/*
So that output would be:
Book 1
M. Estelle
K.J. Shevon
Book 2
Leila Seward*/
$(".book").each(function() {
var authors = $('h3.author', this);
if (authors.length > 1) {
authors.each(function() {
var result = $(this).text().split(' ').map(function(name, index, arr) {
return index < arr.length - 1 ? name[0]+'.' : name;
}).join(' ');
$(this).text(result);
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
<div class="book">
<a> <h2>Book 1</h2>
<h3 class="author">Minty Estelle</h3>
<h3 class="author">Katey Josepha Shevon</h3></a></div>
<div class="book">
<a> <h2>Book 2</h2>
<h3 class="author">Leila Seward</h3></a></div>

Simplify function for removing duplicate array

I want to find div element that contain custom attribute mod than append that div to list item. But first I have to remove divs that contain duplicate mod value. Here's what I have done
<div class="list"></div>
<div class="container">
<div mod="dog"></div>
<div mod="man"></div>
<div mod="woman"></div>
<div mod="dog"></div>
<div mod="bird"></div>
<div mod="insects"></div>
<div mod="dog"></div>
</div>
this is my script
modArr($('.container').find('[mod]'))
function modArr(el){
var filterArray = [] // store mod
, modNames = [] // store mod value
, arrIndex = [] // store non duplicate index
, li = [] // store
modArray = el
// store mod value
for(var i=0; i < modArray.length; i++){
modNames.push($(modArray[i]).attr('mod')) // get mod value from div
}
// search for non duplicate mod value and get the index of none duplicate mod
for(var i=0; i < modArray.length; i++){
if(filterArray.indexOf(modNames[i]) === -1){
filterArray.push(modNames[i])
arrIndex.push(i) // push non duplicate index value
}
}
filterArray = [] // reset filterArray
// push module from modArray to filterArray using index in arrIndex
for(var i=0; i < arrIndex.length; i++){
filterArray.push(modArray[arrIndex[i]])
}
// push to li array
$.each(filterArray,function(i,el){
li[i] = '<li>'+ el.outerHTML +'</li>'
})
$('<ul></ul>')
.append(li.join(''))
.appendTo('.list')
}
What you can see is that I've used to many loops, is there any simple way to do this. Thanks!
We can use an object as a map for checking duplicates, see comments (I've added text to the mod divs so we can see them):
modArr($('.container').find('[mod]'));
function modArr(elements) {
// A place to remember the mods we've seen
var knownMods = Object.create(null);
// Create the list
var ul = $("<ul></ul>");
// Loop the divs
elements.each(function() {
// Get this mod value
var mod = this.getAttribute("mod");
// Already have one?
if (!knownMods[mod]) {
// No, add it
knownMods[mod] = true;
ul.append($("<li></li>").append(this.cloneNode(true)));
}
});
// Put the list in the .list element
ul.appendTo(".list");
}
<div class="list"></div>
<div class="container">
<div mod="dog">dog</div>
<div mod="man">man</div>
<div mod="woman">woman</div>
<div mod="dog">dog</div>
<div mod="bird">bird</div>
<div mod="insects">insects</div>
<div mod="dog">dog</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
We can also just use the DOM to check for duplicates, but it's a bit slower (not that it matters for the number of elements here):
modArr($('.container').find('[mod]'));
function modArr(elements) {
// Create the list
var ul = $("<ul></ul>");
// Loop the divs
elements.each(function() {
// Get this mod value
var mod = this.getAttribute("mod");
// Already have one?
if (ul.find('div[mod="' + mod + '"]').length == 0) {
// No, add it
ul.append($("<li></li>").append(this.cloneNode(true)));
}
});
// Put the list in the .list element
ul.appendTo(".list");
}
<div class="list"></div>
<div class="container">
<div mod="dog">dog</div>
<div mod="man">man</div>
<div mod="woman">woman</div>
<div mod="dog">dog</div>
<div mod="bird">bird</div>
<div mod="insects">insects</div>
<div mod="dog">dog</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Note: I used this.cloneNode(true) rather than outerHTML because there's no need to take a roundtrip through markup. If you want more jQuery there, it's $(this).clone(); ;-) Similarly, if you don't like this.getAttribute("mod"), there's $(this).attr("mod").
I'd be remiss if I didn't point out that mod is an invalid attribute name for div elements. You can use any name you want starting with data-, though, so perhaps use <div data-mod="dog"> instead.
Try this, only adds if an element with mod is not already in list:
$('.list').append('<ul>');
$('.container [mod]').each(function(index, el) {
if($('.list [mod=' + $(el).attr('mod') + ']').length === 0) {
$('.list ul').append($('<li>' + el.outerHTML + '</li>'));
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="list"></div>
<div class="container">
<div mod="dog">Dog1</div>
<div mod="man">Man1</div>
<div mod="woman">Woman1</div>
<div mod="dog">Dog2</div>
<div mod="bird">Bird1</div>
<div mod="insects">Insect1</div>
<div mod="dog">Dog3</div>
</div>

Categories

Resources