How to style my javascript innerHTML into separate components? - javascript

So I'm mapping through an array of objects, but it displays everything within one line.
I want to display this as if it's 2 separate cards.
let data = [
{
name: 'Person 1',
age: '30'
},
{
name: 'Person 2',
age: '30'
},
];
const info = document.querySelector('#info');
let details = data.map(function(item) {
return ' ' + item.name + ' ' + 'is ' + item.age;
});
info.innerHTML = details;
I only have one p tag, so it just displays the name and age all within that one p tag.
<p id="info"></p>
So it just looks like this
Person 1 is 30, Person 2 is 30.
What I'd like it to do is be wrapped in a custom div and essentially be a stand alone card.
So like this

What you could do is create two separate divs or paragraphs (whatever you need), each holding one persons information. Then you could create an array that holds the objects within. Something like this
let data = [
{
name: 'Person 1',
age: '30'
},
{
name: 'Person 2',
age: '30'
},
];
const info = document.querySelector('#info');
const infoTwo = document.querySelector('#info2');
info.innerHTML = data[0].name + ' ' + 'is ' + data[0].age;
infoTwo.innerHTML = data[1].name + ' ' + 'is ' + data[1].age;
<p id = "info"></p>
<p id = "info2"></p>

It'd be more like this. You could just add them to div's... that's very close to how ReactJS does it by using JSX, but by using plain JS, it is:
let data = [{
name: 'Person 1',
age: '30'
},
{
name: 'Person 2',
age: '28'
},
];
const info = document.querySelector('#info');
let details = data.map(function(item) {
return '<div>' + item.name + ' ' + 'is ' + item.age + "</div>";
});
info.innerHTML = details.join("\n");
#info div {
border: 1px dotted #07f;
margin: 0.3em;
padding: 1.2em;
border-radius: 0.6em;
}
<div id="info"></div>
ES6 syntax
Now, it can be made a little cleanly if you use the ES6 syntax, like the following:
let data = [{
name: 'Person 1',
age: '30'
},
{
name: 'Person 2',
age: '28'
},
];
const info = document.querySelector('#info');
let details = data.map(item => `<div>${item.name} is ${item.age}</div>`);
info.innerHTML = details.join("\n");
#info div {
border: 1px dotted #07f;
margin: 0.3em;
padding: 1.2em;
border-radius: 0.6em;
}
<div id="info"></div>

Related

Get text of previous header in HTML

I have a HTML which looks like this:
<h1>Title</h1>
<p>Some additional content, can be multiple, various tags</p>
<h2><a id="123"></a>Foo</h2>
<p>Some additional content, can be multiple, various tags</p>
<h3><a id="456"></a>Bar</h3>
Now, for each anchor with id, I want to find out the header hierarchy, e.g. for the anchor with id="123" I would like to get something like [{level: 1, title: "Title"}, {level: 2, title: "Foo"}], similarly for anchor with id="456", I would like to get [{level: 1, title: "Title"}, {level: 2, title: "Foo"}, {level: 3, title: "Bar"}].
My code looks like this so far:
const linkModel: IDictionary<ILinkModelEntry> = {};
const $ = cheerio.load(html);
$("a").each((_i, elt) => {
const anchor = $(elt);
const id = anchor.attr().id;
if (id) {
const parent = anchor.parent();
const parentTag = parent.prop("tagName");
let headerHierarchy: any[] = [];
if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(parentTag)) {
let level = parseInt(parentTag[1]);
headerHierarchy = [{level, text: parent.text()}];
level--;
while (level > 0) {
const prevHeader = parent.prev("h" + level);
const text = prevHeader.text();
headerHierarchy.unshift({level, text});
level--;
}
}
linkModel["#" + id] = {originalId: id, count: count++, headerHierarchy};
}
});
What am I doing wrong, since
const prevHeader = parent.prev("h" + level);
const text = prevHeader.text();
always returns an empty string (i.e. "")?
If I understand correctly, you're looking to capture hierarchy. If your example had another <h1> followed by more <h2> and <h3>s below it, you'd want to pop the stack of parents back down to that new <h1> level for linking future <h2> and <h3> children rather than have an array of all elements back up to that first <h1>Title</h1>.
Here's one approach:
const cheerio = require("cheerio"); // ^1.0.0-rc.12
const html = `
<h1>Title</h1>
<p>Some additional content, can be multiple, various tags</p>
<h2><a id="123"></a>Foo</h2>
<p>Some additional content, can be multiple, various tags</p>
<h3><a id="456"></a>Bar</h3>
<h1>Another Title</h1>
<h2><a id="xxx"></a>Foo 2</h2>
<h3><a id="yyy"></a>Bar 2</h3>`;
const $ = cheerio.load(html);
const result = {};
const stack = [];
[...$("h1,h2,h3,h4,h5,h6")].forEach(e => {
const level = +$(e).prop("tagName")[1];
while (stack.length && level <= stack.at(-1).level) {
stack.pop();
}
if (!stack.length || level >= stack.at(-1).level) {
stack.push({level, title: $(e).text()});
}
if ($(e).has("a[id]").length) {
const id = $(e).find("a[id]").attr("id");
result[`#${id}`] = [...stack];
}
});
console.log(result);
Output:
{
'#123': [ { level: 1, title: 'Title' }, { level: 2, title: 'Foo' } ],
'#456': [
{ level: 1, title: 'Title' },
{ level: 2, title: 'Foo' },
{ level: 3, title: 'Bar' }
],
'#xxx': [
{ level: 1, title: 'Another Title' },
{ level: 2, title: 'Foo 2' }
],
'#yyy': [
{ level: 1, title: 'Another Title' },
{ level: 2, title: 'Foo 2' },
{ level: 3, title: 'Bar 2' }
]
}
If you actually want the whole chain of ancestors linearly back to the first, then remove the while loop (unlikely your intent).

How to properly check if a name is available in an array made of objects (record collection)?

so my plan for this was to have a message appear asking someone to type in a student name. Javascript would look through a record, which is in a seperate JS file, and then output that in the message variable. If suppose the student didn't exist, the output message would be the alert box in the else statement.
Heres a record of the students:
var students=[
{
name:'Chris',
track:'IOS',
achievements:'100',
points:'1000'
},
{
name:'John',
track:'Web Design',
achievements:'90',
points:'1000'
},
{
name:'Brent',
track:'Front-End',
achievements:'70',
points:'1000'
},
{
name:'Josh',
track:'Full-Stack',
achievements:80,
points:'1000'
},
{
name:'Nick',
track:'AI',
achievements:'60',
points:'1000'
}
];
var message="";
var search=prompt("Type name of student");
while (search!=="quit") {
for (var i=0; i<students.length; i+=1) {
var studentName=students[i].name;
if (studentName===search) {
message+="<h1>"+studentName+"</h1>";
message+="<p>"+student[i].track+"</p>";
message+="<p>"+student[i].achievements+"</p>";
message+="<p>"+student[i].points+"</p>";
break;
} else {
alert("That student does not exist. Try again");
break;
}
}
search=prompt("Type name of student");
}
print(message);
When I try this code, it asks me for the student's name and then says he/she is not available. Apparently, the determination that the student is not in the list should only be made after the loop has finished checking all the students. Then, and only if nothing was found, should the failure message be output.
The problem for me, conceptually, is that the final value of the variable, studentName, after the for loop ends will be the name property of the last object in the array. So how would I redesign my for loop then?
How can I redesign my code to accomplish just that?
You can try this,
var message="";
var search=prompt("Type name of student");
while (search!=="quit") {
// we will get result if any one student name matches
var result = students.find((student) => student.name === search);
if (result) {
message+="<h1>"+result.name+"</h1>";
message+="<p>"+result.track+"</p>";
message+="<p>"+result.achievements+"</p>";
message+="<p>"+result.points+"</p>";
}
else {
alert("That student does not exist. Try again");
}
search=prompt("Type name of student");
}
print(message);
you can filter your list first and then check it like
const students = [
{
name: 'Chris',
track: 'IOS',
achievements: '100',
points: '1000'
},
{
name: 'John',
track: 'Web Design',
achievements: '90',
points: '1000'
},
{
name: 'Brent',
track: 'Front-End',
achievements: '70',
points: '1000'
},
{
name: 'Josh',
track: 'Full-Stack',
achievements: 80,
points: '1000'
},
{
name: 'Nick',
track: 'AI',
achievements: '60',
points: '1000'
}
];
let search = prompt('Type name of student');
while (search !== 'quit') {
const filteredList = students.filter(function(student) {
return student.name === search;
});
let message = '';
if (filteredList.length > 0) {
for (const student of filteredList) {
message += '<h1>' + student.name + '</h1>';
message += '<p>' + student.track + '</p>';
message += '<p>' + student.achievements + '</p>';
message += '<p>' + student.points + '</p>';
}
alert(message);
} else {
alert('That student does not exist. Try again');
}
search = prompt('Type name of student');
}
In order to avoid looping through the entire array each time you want to show a message for the user, making an object from the array is the best approach.
for example:
var students=[
{
id: 1,
name:'Chris',
track:'IOS',
achievements:'100',
points:'1000'
},
{
id: 2,
name:'John',
track:'Web Design',
achievements:'90',
points:'1000'
},
{
id: 3,
name:'Brent',
track:'Front-End',
achievements:'70',
points:'1000'
},
{
id: 4,
name:'Josh',
track:'Full-Stack',
achievements:80,
points:'1000'
},
{
id: 5,
name:'Nick',
track:'AI',
achievements:'60',
points:'1000'
}
];
const arrayToObject = (array) =>
array.reduce((obj, item) => {
obj[item.id] = item
return obj
}, {});
const studentsObject = arrayToObject(students);
console.log(studentsObject);
console.log(studentsObject[2]);

How do I give a form a specific ID for a cart when it's appended

I have a webpage where, if you click on a product name, it opens up the description of the product as well as a form which says 'Add to cart'. This doesn't work.
The same form appears on all of my products so no matter which product I select, it doesn't specify which product I want to add to cart as it's appended to all of the products.
I want it to work so that I can click on a product name, the description and form pops up on the same page (this part is working) but then when I change the quantity to a number then click 'Add to cart' on the form, it has its own unique identifier where I'll actually be able to input it into the cart as its own product.
This is what I have so far, I just haven't created the code for my actual cart yet but as I know that I'm doing something wrong so far as I can see no matter the form I'm using in any product, the output is the same no matter the product.
function drawPage() {
$.get('/products', function(data) {
console.log(data);
var prod = data.products;
for (var i = 0; i < prod.length; i++) {
var el = document.createElement('P');
el.innerHTML = prod[i].name;
el.addEventListener('click', function(event) {
console.log(event.target.textContent);
for (var i = 0; i < prod.length; i++) {
if (prod[i].name == event.target.textContent) {
$('#pro').html('');
$('#pro').html(prod[i].name +
prod[i].description + '<img src=' + prod[i].image_url + '>' + "Price: " +
prod[i].unit_cost);
$("#pro").append(
$("<form/>", {
action: '#',
method: '#'
}).append($("<input/>", {
type: 'number',
id: 'pro',
name: 'productname',
placeholder: 'Quantity'
}), $("<br/>"), $("<input/>", {
type: 'submit',
id: 'submit',
value: 'Add to Cart'
}))
)
}
}
})
$('#app').append(el);
}
})
}
document.addEventListener('load', drawPage());
<div id="cart-container">
<h1>My Cart</h1>
<table id="myCart" border="1">
<tr>
<th>Item name</th>
<th>Qty</th>
<th>Price</th>
<th>Total</th>
</tr>
<tr style="display: none; border-top: 2px solid black">
<td colspan="3">Subtotal</td>
<td class="right subtotal"></td>
</tr>
</table>
</div>
I just assume that your prodcuts are a couple of json formatted objects and then u can just pass them around :
var products = [
{id: 1, name: 'Product 1', desc: 'product 1 desc'},
{id: 2, name: 'Product 2', desc: 'product 2 desc'},
{id: 3, name: 'Product 3', desc: 'product 3 desc'},
{id: 4, name: 'Product 4', desc: 'product 4 desc'},
];
$.each(products, function() {
addProduct(this);
});
function addProduct(product) {
var link = $('<button></button>').text(product.name);
link.on('click', function(){
loadProduct(product);
});
$('#products').append(link);
}
function loadProduct(product) {
$('#product-display').empty();
var desc = $('<div></div>').html(product.name + '<br/>' + product.desc);
var btn = $('<button></button>').text('add').on('click', function() {
addToCart(product);
});
$('#product-display').append(desc).append(btn);
}
function addToCart(product) {
$('#cart').append($("<span></span>").text(product.name));
$('#cart').append("<br/>")
};
The html:
<div id="products"></div>
<div id="product-display"></div>
<div id="cart"></div>
jsfiddle

JavaScript Deeply Nested Array filtering

I have created a somehow functional "deeply" nested array filtering functionality. This functionality displays car, which has a color "RED" assigned to them. However the cars, which "pass" the filter is the car, which only has 1 color assigned. How can ensure that arrays within arrays are being checked by the criteria? Would I require creating a loop within loop? The desired outcome would be that if criteria is set to "RED" it will also display the BMW and SUZUKI as these cars also have this color. Any help, suggestions or further links to other posts to achieve the desired outcome would be truly appreciated as I was not able to find anything very useful on my own. The post, which has been most useful has been this one - Filtering an array with a deeply nested array in JS Below I have attached the code snippet of my current code.
const myCars = [
{ name: "BMW",colour: ["White","Red","Black"] },
{ name: "AUDI",colour: ["Yellow","Silver"] },
{ name: "VW",colour: ["Purple","Gold"] },
{ name: "NISSAN",colour: ["White","Black"] },
{ name: "SUZUKI",colour: ["Red"] },
];
for (x in myCars) {
var keys = Object.keys(myCars[x])
var carSpec = myCars.filter(function(fltr) {
return (fltr.colour == "Red");
});
document.getElementById("show1").innerHTML += carSpec[x].name + " " + "has these colours - " + carSpec[x].colour + "<hr />";
}
<p>Car List.</p>
<p id="show"></p>
<p id="show1"></p>
You need to filter by an includes test over the colour array:
const myCars = [
{ name: "BMW",colour: ["White","Red","Black"] },
{ name: "AUDI",colour: ["Yellow","Silver"] },
{ name: "VW",colour: ["Purple","Gold"] },
{ name: "NISSAN",colour: ["White","Black"] },
{ name: "SUZUKI",colour: ["Red"] },
];
const show1 = document.getElementById("show1");
myCars
.filter(({ colour }) => colour.includes('Red'))
.forEach(({ name, colour }) => {
show1.innerHTML += name + " " + "has these colours - " + colour + "<hr />";
});
<p>Car List.</p>
<p id="show"></p>
<p id="show1"></p>
You can use reduce to get an array of cars which have are available in red , then use forEach to loop over it and display the text.
join will join all the contents of an array by the delimiter
const myCars = [{
name: "BMW",
colour: ["White", "Red", "Black"]
},
{
name: "AUDI",
colour: ["Yellow", "Silver"]
},
{
name: "VW",
colour: ["Purple", "Gold"]
},
{
name: "NISSAN",
colour: ["White", "Black"]
},
{
name: "SUZUKI",
colour: ["Red"]
},
];
let hasRed = myCars.reduce(function(acc, curr) {
if (curr.colour.indexOf('Red') !== -1) {
acc.push(curr)
}
return acc;
}, []).forEach((item) => {
document.getElementById("show1").innerHTML += item.name + " " + "has these colours - " + item.colour.join(',') + "<hr />";
})
<p>Car List.</p>
<p id="show"></p>
<p id="show1"></p>

Is it possible to use a complex json object with a Prototype template?

Say I have an object such as:
{
id: 345,
title: 'Some title',
body: 'Here be a lot of text',
author: {
id: 1
name: Bob
email: bob#example.com
}
}
How would I reference the properties of the author in my template
e.g.,
var template = new Template('
<div class='blog_post'>
<h1><a href='/blog/post/#{id}'>#{title}</a></h1>
<div>#{body}</div>
<div>#{author_name}</div>
</div>
');
Yes it is possible, just use the dot notation, look here http://jsfiddle.net/nBfr8/6/
I rewrite code here:
var o = {
id: 345,
title: 'Some title',
body: 'Here be a lot of text',
author: {
id: 1,
name: 'Bob',
email: 'bob#example.com'
}
};
var template = new Template(
'<div class="blog_post">\n' +
'\t<h1>#{title}</h1>\n' +
'\t<div>#{body}</div>\n' +
'\t<div>#{author.name}</div>\n' +
'</div>'
);
alert(template.evaluate(o));
You also made some syntactic mistakes (like forgetting apostrophes or double quotes), I got rid of them.

Categories

Resources