How do I loop through arrays within arrays? - javascript

I am trying to dynamically create a series of bootstrap cards with returned AJAX data. The AJAX request brings back 12 arrays (see screenshot) but when I try to loop through them and put them on cards only 1 array gets put on all twelve cards (See screenshot)
I'm not sure what I am doing wrong with my loop but hoping someone can help out.
Here is the JS code (sorry about the super long line for the card creation - any advice on how to shorten that would be appreciated).
Below is a reproducible example (the actual data variable is filled by the AJAX call in my code), and here is the JSFiddle:
data = [["The Old Milk Bar", " 144 Dundas Street, Thornbury", "Lorem Ipsum"], ["Little Henri", " 848 High Street, Thornbury", 'Lorem Ipsum'], ["Northern Soul", " 843 High Street, Thornbury", "Lorem Ipsum"]]
window.addEventListener('load', (event) => {
console.log('page is fully loaded');
test(data)
});
const test = function(data) {
console.log(data)
for (i = 0; i < data.length; i++) {
var results = JSON.stringify(data).split('"');
var cafeName = results[1].replace(/[^a-zA-Z0-9éè ]/g, "");
console.log(cafeName)
var cafeAddress = results[3].replace(/[^a-zA-Z0-9éè ]/g, "") + "," + results[2].replace(/[^a-zA-Z0-9éè ]/g, "");
console.log(cafeAddress)
var cafeDescription = results[5];
console.log(cafeDescription)
$(".venue-name").html(cafeName);
$(".venue-address").html(cafeAddress);
$(".venue-description").html(cafeDescription);
$(".share").html('<i class="fas fa-share-alt"></i>');
$(".venue-options").html('<i class="fas fa-ellipsis-h"></i>');
var myCol = $('<div id="col"></div>');
var myPanel = $(
'<div class="card-group"><div class="card card-block m-3 overflow-auto" style="width: 18rem;"><div class="card-body"><h5 class="card-title venue-name"></h5><h6 class="card-subtitle mb-2 text-muted venue-address"></h6><div class="dropdown"><div class="venue-options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></div><div class="dropdown-menu" aria-labelledby="dropdownMenuButton"><a id="share" class="dropdown-item" href="#">Share</a><a id="addToList" class="dropdown-item" href="#">Add to List</a></div></div><div class="venue-description"></div></div></div></div>'
);
myPanel.appendTo(myCol);
myCol.appendTo('#cardList');
};
}

You don't need to call JSON.stringify() and split(). You already have an array, you can access its elements directly.
When you do $(".venue-name").html(cafeName) you're setting the HTML of all elements with that class, not the card you're creating. In fact, it sets every one except the new one, because you don't add that until after those lines.
Instead, create myPanel first, and then set the HTML of the elements within that DIV. You can do this with myPanel.find(".className") or equivalently $(".className", myPanel).
data = [
["The Old Milk Bar", " 144 Dundas Street, Thornbury", "Lorem Ipsum"],
["Little Henri", " 848 High Street, Thornbury", 'Lorem Ipsum'],
["Northern Soul", " 843 High Street, Thornbury", "Lorem Ipsum"]
]
window.addEventListener('load', (event) => {
console.log('page is fully loaded');
test(data)
});
const test = function(data) {
console.log(data)
data.forEach(([cafeName, cafeAddress, cafeDescription]) => {
var myCol = $('<div id="col"></div>');
var myPanel = $(
`
<div class="card-group">
<div class="card card-block m-3 overflow-auto" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title venue-name"></h5>
<h6 class="card-subtitle mb-2 text-muted venue-address"></h6>
<div class="dropdown">
<div class="venue-options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></div>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton"><a id="share" class="dropdown-item" href="#">Share</a><a id="addToList" class="dropdown-item" href="#">Add to List</a></div>
</div>
<div class="venue-description"></div>
</div>
</div>
</div>`
);
$(".venue-name", myPanel).html(cafeName);
$(".venue-address", myPanel).html(cafeAddress);
$(".venue-description", myPanel).html(cafeDescription);
$(".share", myPanel).html('<i class="fas fa-share-alt"></i>', myPanel);
$(".venue-options", myPanel).html('<i class="fas fa-ellipsis-h"></i>');
myPanel.appendTo(myCol);
myCol.appendTo('#cardList');
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="cardList"></div>

Related

document.querySelector keeps returning null in function

I have a span element with an id="edit-..." that is in a card like this. This card is dynamically added to the DOM by the way:
<div class="d-flex w-100 justify-content-between" style="pointer-events:none;">
<h5 class="mb-1" style="pointer-events:none;">${post.creator}</h5>
<small>Likes <span id="like-count-${post.id}" class="badge badge-primary badge-pill" style="pointer-events:none">${post.likes}</span></small>
</div>
<p class="mb-1" style="pointer-events:none;">${post.content}</p>
<div class="d-flex w-100 justify-content-between" style="pointer-events=none;">
<small>${post.created}&#8287&#8287<span id="edit-${post.id}">&#8226 EDIT</span></small>
<div name="like-btn" id="${post.id}">
</div>
</a>
When I try to access that span element directly from the console like below it works perfectly fine:
> document.querySelector('span[id="edit-4"]')
or I even tried out this and it still worked:
> const tweet = 4
> document.querySelector(`span[id="edit-${tweet}"]`)
However, when I try and do it from a function and print to console it keeps returning null and I cannot understand why?
function editButton(creator, post){
const currentUser = document.querySelector('#signed-in-user').innerText;
const tweet = post
if(creator != currentUser){
const editBtn = document.querySelector(`span[id="edit-${tweet}"]`);
console.log(document.querySelector(`span[id="edit-${tweet}"]`));
console.log("Post (" + post + "): " + creator + " does not match logged in user");
}
}
I even added this extra line to my function:
console.log(`edit-${tweet}`)
to see if tweet was returning the number correctly and it is. Can anyone see what's going on here?

JQUERY/AJAX-multiply classes based on content number

I'm trying to extract specific data and send them to the client side so in order to achieve that I used AJAX like below :
<script type ="text/Javascript">
$(document).ready(() => {
$.ajax({
type: 'GET',
dataType : 'json',
url: '/envoi/events/',
})
.done(function(data) {
for(let i =0; i< data.length;i++) {
console.log(data[i].events.eventName);
$("#event1").html(`<b>${data[i].events.eventName}</b>`) // event1 is the id of field name
$("#time1").html(`<b>${data[i].events.eventDate} - ${data[i].events.targetReminder} |
${data[i].events.targetAmPM} </b>`)// time1is the id of field time and date
$("#comment1").html(`<b>${data[i].events.caption}</b>`) // comment1 is the id of field description
$("#location1").html(`<b>${data[i].events.location}</b>`) // location1 is the id of field location
}
})
.fail(function(xhr, status, error) {
console.log(error);
})
.always(function(data){
});
})
</script>
and this is the output
this script works perfectly and fill one container with the requested values but what I'm looking for is to fill every container and multiply them based on data found from my db
which mean :
1- if there is more than one data data[i].events.eventName or other another container must be created and get filled by the new value
the HTML code is below :
<div class="card">
<div class="card-header" id="headingOne-1">
<script type ="text/Javascript">
$(document).ready(() => {
$.ajax({
type: 'GET',
dataType : 'json',
url: '/envoi/events/',
})
.done(function(data) {
for(let i =0; i< data.length;i++) {
console.log(data[i].events.eventName);
$("#event1").html(`<b>${data[i].events.eventName}</b>`)
$("#time1").html(`<b>${data[i].events.eventDate} - ${data[i].events.targetReminder} | ${data[i].events.targetAmPM} </b>`)
$("#comment1").html(`<b>${data[i].events.caption}</b>`)
$("#location1").html(`<b>${data[i].events.location}</b>`)
}
})
.fail(function(xhr, status, error) {
console.log(error);
})
.always(function(data){
});
})
</script>
<div class="event-time">
<time id="time1" datetime="2004-07-24T18:18">9:00am</time>
<div class="more"><svg class="olymp-three-dots-icon"><use xlink:href="svg-icons/sprites/icons.svg#olymp-three-dots-icon"></use></svg>
<ul class="more-dropdown">
<li>
Mark as Completed
</li>
<li>
Delete Event
</li>
</ul>
</div>
</div>
<h5 class="mb-0 title">
<a href="#" data-toggle="collapse" data-target="#collapseOne-1" aria-expanded="true" aria-controls="collapseOne" id = "event1">
Breakfast at the Agency
<i class="fa fa-angle-down" aria-hidden="true"></i>
<span class="event-status-icon" data-toggle="modal" data-target="#public-event">
<svg class="olymp-calendar-icon" data-toggle="tooltip" data-placement="top" data-original-title="UNCOMPLETED"><use xlink:href="svg-icons/sprites/icons.svg#olymp-calendar-icon"></use></svg>
</span>
</a>
</h5>
</div>
<div id="#collapseOne-1" class="collapse show" aria-labelledby="headingOne" data-parent="#headingOne-1">
<div class="card-body" id ="comment1">
Hi Guys! I propose to go a litle earlier at the agency to have breakfast and talk a little more about the new design project we have been working on. Cheers!
</div>
<div class="place inline-items">
<svg class="olymp-add-a-place-icon"><use xlink:href="svg-icons/sprites/icons.svg#olymp-add-a-place-icon"></use></svg>
<span id ="location1">Daydreamz Agency</span>
</div>
</div>
</div>
Any idea how to multiply that box based on data found with the script mentioned above ?
Hope I mentioned everything :-D ?
Best Regards,
Every time your loop works, $("#event1").html(....), $("#-----").html(....) will be replaced with your new values. So why don't you rather create a variable name html outside a loop and every div section or html tags that needs to be rendered in DOM, inside the loop and append after the div you want to render. Like mentioned on above answer,
var html="";
for(let i =0; i< data.length;i++){
html += `<div class="card-header"> ${data[i].event.eventName} </div>` +
`<div class=" -----"> $${data[i].event.eventDate} ` +
------------------and so on---------------------------;
}
$("#NAME OF ID BEHIND YOU WANT TO SHOW YOUR NEW DIV").append(html);
Hope it will work :)
I believe you should just create the whole HTML in javascript then append that HTML to DOM. for example.
let html = "";
for(let i =0; i< data.length;i++) {
html += "<p>"+ data[i].events.eventName + "</p>";
html += "<p>"+ data[i].events.eventDate + "</p>";
}
$("#ParentDivIDWhereToAppendThisContent").html(html);
The above code is just an example of how you can do it. You need to customize it to suit your needs.

Multiple HTML files linked to only one JS file

I have 5 html files with very similar content: it's a cv builder website and I am making 5 different cv templates. What I was trying to do is to create a javacript class which will instance 5 different objects from the class with different values.
But when I link the same js file with all the 5 html pages, an error occurs and the innerHTML cannot be read. Here is a piece of the code:
let resume1Div = document.querySelector(".resume-web-development");
let resume2Div = document.querySelector(".resume-data-science");
class resumeHtml {
constructor(_title, _jobPosition, _summary, _image, _email, _phone, _address, _twitter) {
this.title = _title;
this.jobPosition = _jobPosition;
this.summary = _summary;
this.image = _image;
this.email = _email;
this.phone = _phone;
this.address = _address;
this.twitter = _twitter;
}
}
let resume1 = new resumeHtml("Elon Musk", "Enterpreneur, Engineer, Inventor and Investor", "Aiming to reduce global warming through sustainable energy production and consumption. Planning to reduce the risk of human extinction by making life multi-planetary and setting up a human colony on Mars.", "assets/images/musk.jpg", "elon#teslamotors.com", "620-681-5000", "Los Angeles, USA", "#elonmusk");
let resume2 = new resumeHtml("John Doe", "Data Scientist", "Highly accurate and experienced Data Scientist adept at collecting, analyzing and interpreting large datasets, developing new forecasting models, and performing data management tasks.", "assets/images/john.jpg", "john#gmail.com", "6782092", "Ontario, Canada", "#johnDoe");
let resume1Arr = [resume1];
let resume2Arr = [resume2];
resume1Arr.forEach(el => {
resume1Div.innerHTML +=
`
<div class="resume-title col-md-10">
<span></span>
<h2>${el.title}</h2>
</div>
<div class="col-md-5 resume-description">
<h4 class="resume-job-position">${el.jobPosition}</h4>
<p class="resume-summary">${el.summary}</p>
</div>
<div class="col-md-2 resume-img">
<img src=${el.image} class="img-circle">
</div>
<div class="col-md-3 col-md-offset-2 resume-contact-details">
<span>${el.email}</span><i class="fa fa-envelope" aria-hidden="true"></i><br>
<span>${el.phone}</span><i class="fa fa-mobile" aria-hidden="true"></i><br>
<span>${el.address}</span><i class="fa fa-map-marker" aria-hidden="true"></i><br>
<span>${el.twitter}</span><i class="fa fa-twitter" aria-hidden="true"></i>
</div>
`
});
resume2Arr.forEach(el => {
resume2Div.innerHTML +=
`
<div class="resume-title col-md-10">
<span></span>
<h2>${el.title}</h2>
</div>
<div class="col-md-5 resume-description">
<h4 class="resume-job-position">${el.jobPosition}</h4>
<p class="resume-summary">${el.summary}</p>
</div>
<div class="col-md-2 resume-img">
<img src=${el.image} class="img-circle">
</div>
<div class="col-md-3 col-md-offset-2 resume-contact-details">
<span>${el.email}</span><i class="fa fa-envelope" aria-hidden="true"></i><br>
<span>${el.phone}</span><i class="fa fa-mobile" aria-hidden="true"></i><br>
<span>${el.address}</span><i class="fa fa-map-marker" aria-hidden="true"></i><br>
<span>${el.twitter}</span><i class="fa fa-twitter" aria-hidden="true"></i>
</div>
`
});
Resume1Div is the div in one html file, Resume2Div is the div in the another html file.
How can I use the same js file for all the html files just by changing the values, without making 5 separate js files with the same code?
I assume on some of your five pages you got the div with class resume-web-development on the others the div with class resume-data-science.
That leads to one variable referencing a DOM element and one variable empty in each case.
As your code is basically well structured you just have to check whether it is empty as in:
if (resume1Div) resume1Arr.forEach(el => {
resume1Div.innerHTML += ...
}
if (resume2Div) resume2Arr.forEach(el => {
resume2Div.innerHTML += ...
}
That should already do the trick.
You can then optimise the code with something like:
function fillDiv(div, el){
div.innerContent+= ...
}
let resumeDiv = resume1Div || resume2Div;
fillDiv(resumeDiv, resumeDiv === resume1Div ? resume1 : resume2);
Might still be improved.

Load More Button JS - Load new content on page

I'm trying to develop a Load-More button using Javascript calling an API done with PHP.
So far, so good. I can load the new objects in a range i += i + 4 (I load three new comments everytime I press the button).
This is my load-more button:
$(document).ready(function () {
$(".load-more").on('click', function () {
var tab = $(this).data('tab');
var next_page = $(this).data('next-page');
console.log(next_page);
console.log(tab);
$.get($(this).data('url') + '?tab=' + tab + '&page=' + next_page, function (data) {
addNewQuestions($.parseJSON(data));
});
});
});
And for every object loaded I want to print each of these html blocks.
<div class="question-summary narrow">
<div class="col-md-12">
<div class="votes">
<div class="mini-counts"><span title="7 votes">
{if $question['votes_count']}
{$question['votes_count']}
{else}
0
{/if}
</span></div>
<div>votes</div>
</div>
<div {if $question['solved_date']}
class="status answered-accepted"
{else}
class="status answer-selected"
{/if}
title="one of the answers was accepted as the correct answer">
<div class="mini-counts"><span title="1 answer">{$question['answers_count']}</span></div>
<div>answer</div>
</div>
<div class="views">
<div class="mini-counts"><span title="140 views">{$question['views_counter']}</span></div>
<div>views</div>
</div>
<div class="summary">
<h3>
<a href="{questionUrl($question['publicationid'])}" class="question-title" style="font-size: 15px; line-height: 1.4; margin-bottom: .5em;">
{$question['title']}
</a>
</h3>
</div>
<div class = "statistics col-sm-12 text-right" style="padding-top: 8px">
<span>
<i class = "glyphicon glyphicon-time"></i>
<span class="question-updated-at">{$question['creation_date']}</span>
</span>
<span>
<i class = "glyphicon glyphicon-comment"></i>
<span class="question-answers">{$question['answers_count']}</span>
</span>
</div>
</div>
</div>
The problem is that I have several conditions, as {if$question['votes_count']} and I'm struggling because I don't know how get those variables when rendering the html.
Then I found something but I can't figure out how to adapt to my case
On that addNewQuestions I think that a good approach would be:
function addNewQuestions(objects) {
$.each(objects, function (i, object) {
console.log(object);
var lastItem = $('div.active[role="tabpanel"] .question-line:last');
var newLine = lastItem.clone(true);
var newObject = newLine.find('.question-col:eq(' + i + ')');
newObject.find('.question-info-container').attr('data-id', object.publicationid);
newObject.find('.vote-count').html(object.votes);
updateTitleAndLink(newObject.find('.question-title'), object);
lastItem.after(newLine);
});
}
function updateTitleAndLink(questionTitle, object) {
questionTitle.attr('href', questionTitle.data('base-question-url') + object.publicationid);
questionTitle.html(object.title);
}
But nothing happens and I can't figure out why.
Any idea or suggestion?
Kind regards

jQuery on click function or random quote machine and JSON API

For the first time using JSON API. Please tell me where and what I'm doing wrong. This is a challenge from freeCodeCamp. We need to do build a random quote machine.
Once I click on New Quote button is should give us a random quote. In jQuery I'm looping through json and on click function has to change me current h2 class = text with the new random quote from JSON.
Project is here http://codepen.io/ekilja01/full/Lbdbpd/.
Please help.
Here is my HTML:
<div class="container-fluid">
<div class = "well">
<div class="row">
<h2 class="text text-center"><i class="fa fa-quote-left"> </i> Hey, what when and why there is no and yes?</h2>
<p class="author">-Alina Khachatrian</p>
<div class="buttons">
<div class="row">
<div class="col-xs-6">
<a id="tweet-quote" title="Tweet current quote" target="_blank" href="#">
<i class="fa fa-twitter fa-2x"></i>
</a>
</div>
<div class="col-xs-6">
<button type="button" class="btn btn-default btn-transparent" id ="getNewQuote" title="Get a new quote">New Quote</button>
</div>
</div>
<footer class="text-center">
<hr>
<p>Written and coded by Edgar Kiljak.</p>
</footer>
</div>
</div>
and JS:
$(document).ready(function(){
$(".btn").on("click", function(){
$.getJSON("http://quotes.rest/qod.json", function (json) {
var html = "";
json.forEach(function(val){
var keys = Object.keys(val);
html += "<div class = 'newQuote>'";
"<h2 = '" + val.quote + "'>";
html += "</h2>";
html += "</div>";
})
$(".text").html(html);
});
});
});
and JSON:
{
"success": {
"total": 1
},
"contents": {
"quotes": [
{
"quote": "Great things are done by a series of small things brought together.",
"length": "67",
"author": "Vincent Van Gogh",
"tags": [
"inspire",
"small-things",
"tso-art"
],
"category": "inspire",
"date": "2016-12-10",
"title": "Inspiring Quote of the day",
"background": "https://theysaidso.com/img/bgs/man_on_the_mountain.jpg",
"id": "DLThmumKP4CCe1833rRvNQeF"
}
]
}
}
your problem is here: json.forEach(function(val)...
since the JSON is not an array, it should be: json.contents.quotes.forEach(function(val)
json.contents.quotes is an array ([brackets] instead of {}) and forEach is only for arrays
Please post the error you are getting.
Also in your .js file, you are doing :
"<h2 = '" + val.quote + "'>";
which should be
"<h2>'" + val.quote + "'</h2>";
Another advice would be to put the code where you are handling the response in .done(). This method as far as I know, is available with the $.get() method.
From the jQuery Docs,
$.get( "test.cgi", { name: "John", time: "2pm" } )
.done(function( data ) {
alert( "Data Loaded: " + data );
});

Categories

Resources