First and second name initials, preserving full surname - javascript

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>

Related

Group 'for loop' output into groups of four

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);

Hide parent element based on value of child element

I want to create a filter function which hides divs based on values of p tags. The behavior should be the following:
User selects a filter, e.g. >5:
Loop through all p-tags with a certain class
If the value within the p-tag matches the filter (>5), hide all parent divs of each p-tag which value doesn't match the filter value
My solution is the following:
function eraseThis() {
counter = 0
tagList = document.getElementsByClassName("rating")
$(".rating").each(function()
{
if (this.innerHTML < 5) {
$(this).parent().hide()
}
counter = counter + 1
});
}
This gives me the results I'm looking for but I'm wondering if there's a more elegant / efficient way to do it ?
The markup would be something like:
<div class="movie">
<p class="rating"> some value </p>
</div>
<div class="movie">
<p class="rating"> some value </p>
</div>
and so on
I think this should do the trick :
JQuery code :
function eraseThis() {
var tagList = $(".rating");
tagList.each(function(){
var $this = $(this);
if (parseInt( $this.text() ) < 5) {
$this.parent().hide();
}
});
}
Html code :
<div class="movie">
<p class="rating">2</p>
</div>
<div class="movie">
<p class="rating">9</p>
</div>
And here is the full code : JSFiddle

Consume formatted string into three different elements

I have code that cuts up a tilte that is formatted like:
TITLE1 - TITLE2 [XXX]
With the three parts getting "parsed" into an array of three pieces and inserted into three div elements:
<div class='title'>
<div class='title2'>
<div class='cut'>
That code is:
var Enumber1 = new Array();
$("#title").each(function(i){
var text = $(this).text();
if(text.indexOf('[') != -1 || text.indexOf(']') != -1){
var Ntext1 = text.split('[')[0];
Enumber1[i] = text.split('[')[1].split(']')[0];
$(this).text(Ntext1);
}
});
$("#cut").each(function(i){
$(this).fadeIn("slow");
if(Enumber1[i] != undefined){
$(this).text(Enumber1[i]);
} else {
$(this).css('N/A');
}
});
So far, I get:
TITLE1 - TITLE2 >>> stay in div class="title"
XXX >>>> cut div class"cut"
What I want is to get this text into:
TITLE1 >>> Stays in <div class"title">
TITLE2 >>> Moves to <div class"title2">
XXX >>> Moves to <div class"cut">
So that the final HTML looks like this:
<div class='title'>
TITLE1
</div>
<div class='title2'>
TITLE2
</div>
<div class='cut'>
XXX
</div>
See https://jsfiddle.net/bro69zvb/6/ for my current code.
You can simply use a split function at the end of your code:
var Enumber1 = new Array();
$(".title").each(function(i){
var text = $(this).text();
if(text.indexOf('[') != -1 || text.indexOf(']') != -1){
var Ntext1 = text.split('[')[0];
Enumber1[i] = text.split('[')[1].split(']')[0];
$(this).text(Ntext1);
}
});
$(".cut").each(function(i){
$(this).fadeIn("slow");
if(Enumber1[i] != undefined){
$(this).text(Enumber1[i]);
}else{
$(this).css('N/A');
}
});
// Get the text from <div class="title>
var titleText = $(".title").text();
//TITLE:
// Get the text before the " - "
title = titleText.split(" - ")[0];
// Put the result inside <div class="title">
$(".title").html(title);
//TITLE2:
// Get the text after the " - " before the next space
title2 = titleText.split(" - ")[1].split(" ")[0];
// Put the result inside <div class="title2">
$(".title2").html(title2);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="title">
Tile1 - Tile2 [XXX]
</div>
<div class="title2">
</div>
<div class="cut">
</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>

Simpler Javascript counter

I need some help with the click event, I'm trying to have an individual counter that is incremented by the click event that I have on the img. I've tried many variations, I want to resolve this without using jQuery.
<script async>
var count = 0;
var clickerCount = document.getElementsByClassName('clicker');
var cat = {
count : 0,
counter: function(){
this.count++;
clickerCount.textContent = "Kitten Click Count :" + this.count;
console.log("counter function working");
console.log(cat.count);
}
};
function modifyNum(){
cat.counter();
console.log("modifyNum function working");
}
</script>
</head>
<body>
<div style="display:inline">
<div>
<img src="http://placekitten.com/200/296" id="cat0" onclick="modifyNum();">
<p id='clicker'>Kitten Click Count :</p>
</div>
<div>
<img src="http://placekitten.com/200/296" id='cat1' onclick="modifyNum();">
<p id='clicker'>Kitten Click Count :</p>
</div>
</div>
For a start, you are using id='clicker' in two places (IDs are supposed to be unique), and then using document.getElementsByClassName, which returns nothing because you used an ID and not a class.
Once you do change it to a class, document.getElementsByClassName will return an array of elements. You'll have to use clickerCount[0] and so on, or loop through the array.
This example should work. I've separated the HTML from the Javascript because it looks clearer for me. You can use it as an example to expand / create your own in your own way.
Hope it help
HTML:
<div style="display:inline">
<div>
<img src="http://placekitten.com/200/296" id="1" class="countable">
<span>Kitten Click Count :</span><span id="counter-for-1">0</span>
</div>
<div>
<img src="http://placekitten.com/200/296" id="2" class="countable">
<span>Kitten Click Count :</span><span id="counter-for-2">0</span>
</div>
</div>
JS:
var imagesCountable = document.getElementsByClassName("countable");
var counters = [];
for (var i = 0; i < imagesCountable.length; i++) {
counters[imagesCountable[i].id] = 0;
imagesCountable[i].onclick = function(e) {
document.getElementById("counter-for-" + e.currentTarget.id)
.innerHTML = ++counters[e.currentTarget.id];
}
}
var imagesCountable = document.getElementsByClassName("countable");
var counters = [];
for (var i = 0; i < imagesCountable.length; i++) {
counters[imagesCountable[i].id] = 0;
imagesCountable[i].onclick = function(e) {
var cElem = document.getElementById("counter-for-" + e.currentTarget.id);
cElem.innerHTML = ++counters[e.currentTarget.id];
}
}
<div style="display:inline">
<div>
<img src="http://placekitten.com/200/296" id="1" class="countable">
<span>Kitten Click Count :</span><span id="counter-for-1">0</span>
</div>
<div>
<img src="http://placekitten.com/200/296" id="2" class="countable">
<span>Kitten Click Count :</span><span id="counter-for-2">0</span>
</div>
</div>
I have solved this problem in this JSFiddle!
If you can hardcode the IDs then it's easier in my point o view to just manipulate things by ID.
<div>
<img src="http://placekitten.com/200/296" id="cat0" onclick="counter(0);">
<p id='clicker0'>Kitten Click Count :</p>
<input type="hidden" id="counter0" value="0">
</div>
function counter(id) {
var cnt = parseInt(document.getElementById("counter" + id).value);
cnt++;
document.getElementById("counter" + id).value = cnt;
document.getElementById('clicker' + id).innerHTML = 'Kitten Click Count :' + cnt;
}
It's not the same approach but I find it easy to understand.
Hope it helps.
Ok, so first off you have two elements with the id of 'clicker'. You probably meant for those to be classes and ids. So when you call modifynum() it cant locate those because the class doesn't exists. Second, your JS is loading before your HTML elements. So when the JS gets to this line:
var clickerCount = document.getElementsByClassName('clicker');
It is going to find nothing, even if you correct the class names. So you want to move your JS to the footer of your HTML document, or wrap the code in a method that is called on pageLoad().
I think that should take care of it. Your object, for the most part, looks correct.

Categories

Resources