Break some html code into Array of objects - javascript

I am trying to analyse some html code and break it into an array of objects.
Here is some example html code:
<slide data-time=5>
<div class="cds-block-title">Master Calendar</div>
<div class="cds-block-content">iframe to master calendar</div>
</slide>
<slide data-time=5>
<div class="cds-block-title">Weather</div>
<div class="cds-block-content">iframe to master Weather App</div>
</slide>
My goal is to break it down into an object similar to this:
[
{
"html":"<slide.....</slide>",
"time":"5",
"title":"Master Calendar",
"content":"iframe...."
},
{
"html":"<slide.....</slide>",
"time":"5",
"title":"Master Calendar",
"content":"iframe...."
}
]
I have tried a few different approaches.
Using Regex (This worked in my test, but not when I put it in production, the .match stopped working as expected, I also read a few posts stating that using regex to parse html code is not the best approach):
function splitSlidesHtml(html){
var html = '<slide data-time="5"><div class="cds-block-title">Activities & Sports</div><div class="cds-block-content">content</div></slide><slide data-time="5"><div class="cds-block-title">weather</div><div class="cds-block-content">content</div></slide>"';
var slides = html.match(/<slide.*?>(.*?)<\/slide>/g);
var slidesA = [];
if (!slides) {
slidesA.push({"html":html});
} else {
for (i in slides){
var c = {};
c.html = slides[i];
c.time = slides[i].match(/(data-time=)(.*?)>/)[2].replace(/['"]+/g, ''); // extract the time, and replace any quotes that might be around it
c.title = slides[i].match(/<div class="cds-block-title">(.*?)<\/div>/)[1];
c.content = slides[i].match(/<div class="cds-block-content">(.*?)<\/div>/)[1];
slidesA.push(c);
}
}
return slidesA;
} // end splitSlidesHtml
I have also tried using jQuery, which kind-of works, but I don't know enough about parseHTML to know how to make sure it breaks at the different slides.
var slides = $.parseHTML(html);
console.log(slides);
console.log(slides[0].innerHTML);
console.log(slides[0].outerHTML);

You can use $.parseHTML() to convert your HTML string into an array of DOM nodes and then loop over the nodes to grab the information you need. .map() is a good use in this case as you are mapping each node to something else.
var html = '<slide data-time=5>\
<div class="cds-block-title">Master Calendar</div>\
<div class="cds-block-content">iframe to master calendar</div>\
</slide>\
<slide data-time=5>\
<div class="cds-block-title">Weather</div>\
<div class="cds-block-content">iframe to master Weather App</div>\
</slide>';
var slides = $($.parseHTML(html)).map(function () {
return {
// store the HTML
html: this.outerHTML,
// store the data-time attribute
time: this.dataset.time,
// store the title
title: $('.cds-block-title', this).text(),
// store the content
content: $('.cds-block-content', this).text(),
};
}).get();
console.log(slides);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

This is what I finally came up with. I had trouble with map working to get the time correctly.
var html = sheetData['values'][prop]['html'];
var parsed = $.parseHTML(html);
var isSlide = true;
for (n in parsed){
var cur = parsed[n];
if (cur.nodeName == "SLIDE"){
var curSlide = {
html: cur.outerHTML, // store the HTML
time: cur.dataset.time, // store the data-time attribute
title: $('.cds-block-title', cur).html(), // store the title
content: $('.cds-block-content', cur).html(), // store the content
};
} else {
isSlide = false;
}
}

Related

How to display multiple images in a html div, using javascript's array and loop

I want to display multiple images in an HTML <div>, so that user can select select the 'avatar' for their profile. I have written this javascript, but it displays only last image in array.
const array1 =[
"Multiavatar1.png",
"Multiavatar2.png", "Multiavatar3.png", "Multiavatar4.png", "Multiavatar5.png", "Multiavatar6.png", "Multiavatar7.png", "Multiavatar8.png", "Multiavatar9.png", "Multiavatar10.png", "Multiavatar11.png", "Multiavatar12.png", "Multiavatar13.png", "Multiavatar14.png", "Multiavatar15.png", "Multiavatar16.png", "Multiavatar17.png", "Multiavatar18.png", "Multiavatar19.png", "Multiavatar20.png", "Multiavatar21.png", "Multiavatar22.png", "Multiavatar23.png", "Multiavatar24.png", "Multiavatar25.png", "Multiavatar26.png", "Multiavatar27.png", "Multiavatar28.png", "Multiavatar29.png", "Multiavatar30.png", "Multiavatar31.png", "Multiavatar32.png", "Multiavatar33.png", "Multiavatar34.png", "Multiavatar35.png", "Multiavatar36.png", "Multiavatar37.png", "Multiavatar38.png", "Multiavatar39.png", "Multiavatar40.png", "Multiavatar41.png", "Multiavatar42.png",
];
array1.forEach( element => {
var image = `<img src="avatar/${element}" alt="img">`;
document.getElementById('avatar_div').innerHTML = image;
});
But apparently I tried using the below script in a separate file, and this worked.
const array1 =[ "Multiavatar1.png",
"Multiavatar2.png",
"Multiavatar3.png",
"Multiavatar4.png", "Multiavatar5.png", "Multiavatar6.png", "Multiavatar7.png", "Multiavatar8.png", "Multiavatar9.png", "Multiavatar10.png", "Multiavatar11.png", "Multiavatar12.png", "Multiavatar13.png", "Multiavatar14.png", "Multiavatar15.png", "Multiavatar16.png", "Multiavatar17.png", "Multiavatar18.png", "Multiavatar19.png", "Multiavatar20.png", "Multiavatar21.png", "Multiavatar22.png", "Multiavatar23.png", "Multiavatar24.png", "Multiavatar25.png", "Multiavatar26.png", "Multiavatar27.png", "Multiavatar28.png", "Multiavatar29.png", "Multiavatar30.png", "Multiavatar31.png", "Multiavatar32.png", "Multiavatar33.png", "Multiavatar34.png", "Multiavatar35.png", "Multiavatar36.png", "Multiavatar37.png", "Multiavatar38.png", "Multiavatar39.png", "Multiavatar40.png", "Multiavatar41.png", "Multiavatar42.png",
];
array1.forEach( element => {
var h = `<img src="avatar/${element}" alt="img"><br>`;
document.write(h);
});
Can anyone explain me, what am
I doing wrong in first script.
And suggestions for my code to work?
Your first sample is not working because you are overwriting the previous HTML.
Your second sample is working because document.write appends the HTML.
To make the first sample work, use += (addition assignment operator) instead of =:
const array1 = [
"Multiavatar1.png",
"Multiavatar2.png", "Multiavatar3.png", "Multiavatar4.png", "Multiavatar5.png", "Multiavatar6.png", "Multiavatar7.png", "Multiavatar8.png", "Multiavatar9.png", "Multiavatar10.png", "Multiavatar11.png", "Multiavatar12.png", "Multiavatar13.png", "Multiavatar14.png", "Multiavatar15.png", "Multiavatar16.png", "Multiavatar17.png", "Multiavatar18.png", "Multiavatar19.png", "Multiavatar20.png", "Multiavatar21.png", "Multiavatar22.png", "Multiavatar23.png", "Multiavatar24.png", "Multiavatar25.png", "Multiavatar26.png", "Multiavatar27.png", "Multiavatar28.png", "Multiavatar29.png", "Multiavatar30.png", "Multiavatar31.png", "Multiavatar32.png", "Multiavatar33.png", "Multiavatar34.png", "Multiavatar35.png", "Multiavatar36.png", "Multiavatar37.png", "Multiavatar38.png", "Multiavatar39.png", "Multiavatar40.png", "Multiavatar41.png", "Multiavatar42.png",
];
array1.forEach(element => {
var image = `<img src="avatar/${element}" alt="img">`;
document.getElementById('avatar_div').innerHTML += image;
});
Demo:
const array1 = [
"https://www.gravatar.com/avatar/0fdacb141bca7fa57c392b5f03872176?s=48&d=identicon&r=PG&f=1",
"https://www.gravatar.com/avatar/0fdacb141bca7fa57c392b5f03872176?s=48&d=identicon&r=PG&f=1"
];
array1.forEach(element => {
var image = ` <img src = "${element}" alt = "img">`;
document.getElementById('avatar_div').innerHTML += image;
});
``
<div id="avatar_div"></div>

What is the correct syntax for this JS loop including a lot of document.getElementById?

I am making a tournament table on HTML and JS. I have a couple of hundred of « match boxes » to which I want to assign a range of different datas (such as a logo/flag, name of the team, score and more).
How could I write a loop that would execute the following potential thousands of lines :
document.getElementById("matchBox1_team1").innerHTML = teams[1].name;
document.getElementById("matchBox1_flag1").innerHTML = teams[1].flag;
document.getElementById("matchBox1_team2").innerHTML = teams[2].name;
document.getElementById("matchBox1_flag2").innerHTML = teams[2].flag;
document.getElementById("matchBox2_team1").innerHTML = teams[3].name;
document.getElementById("matchBox2_flag1").innerHTML = teams[3].flag;
document.getElementById("matchBox2_team2").innerHTML = teams[4].name;
document.getElementById("matchBox2_flag2").innerHTML = teams[4].flag;
etc…
something like this but I havent got the syntax right :
var j=1;
for (i=0;i<1000;i+=2){
document.getElementById("matchBox("j")_team1").innerHTML = teams[i].name;
document.getElementById("matchBox("j")_flag1").innerHTML = teams[i].flag;
document.getElementById("matchBox("j")_team2").innerHTML = teams[i+1].name;
document.getElementById("matchBox("j")_flag2").innerHTML = teams[i+1].flag;
j++}
Thank you!
It really depends on your method of retrieving data. If you are using ajax then create table rows with js and append those rows to the table so you will only have to get only a single element.
var matchesTable = document.getElementById('matches');
function addMatch(name,flag){
var row = document.createElement('tr');
var nameColumn = document.createElement('th');
nameColumn.innerText = name;
var flagColumn = document.createElement('th');
flagColumn.innerText = flag;
row.append(nameColumn);
row.append(flagColumn);
matchesTable.append(row);
}
Wherever you get that data just call addMatch function and pass all the details as parameters and create th elements for all those details and append to that table.
Else you will need thousands of those rows already created for you to get them and then add those details.
Using id attributes with sequential numbers is a code smell: it is bad practice. Instead you should use classes where their sequence follows from their natural order in the DOM.
For instance:
let teams = [
{ name: "New York Knicks", flag: "flagged" },
{ name: "Toronto Raptors", flag: "cleared" },
{ name: "Sacramento Kings", flag: "cleared" },
{ name: "Miami Hear", flag: "pending" }
];
let teamElems = document.querySelectorAll(".matchBox .team");
let flagElems = document.querySelectorAll(".matchBox .flag");
teams.forEach(({name, flag}, i) => {
teamElems[i].textContent = name;
flagElems[i].textContent = flag;
});
.matchBox { border: 1px solid }
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
<div class="matchBox">
<div class="team"></div>
<div class="flag"></div>
</div>
You should use appendChild method into the for loop you have created.
Also one hint is you have to create the element (createElement) which you want to insert in the for loop and insert it in the table you have created using appendChild.
You can refer to syntax on w3cschool

Feed Outside JS object into Vue.js component

In my JS I have list of products.
var productData = [{
title: 'Bike',
price: 300
},{
title: 'Guitar',
price: 199
}]
This data is gathered from remote Json in a Async call. So I have to do this in JS.
Eventually, I come to a situation, that I need to display this product. But here are 2 things.
1) In HTML I am outside of my main Vue instance scope. It is so because there is 3rd party API that interacts with those products, apart other things... So This array is not bound to Vue instance in any way.
2) This product list is dynamically created, on the fly. In order to display it, I have to do a good old html string concatenation and then apply it.
Main problem:
Now whenever I need to display a product - I have a Vue.js Component for it. That has a template, and all data goes really nicely. I obviously want to reuse this same template.
So right now I have something like this:
//Vue.js Component, that I want to use, and feed my product to:
Vue.component('product', {
props: ['product'],
template: `
<div class="prod">
<h3>{{product.title}}</h3>
<p>{{product.price}} €</p>
</div>
`, data() {
return {
}
}
});
Next in JS, I have this code:
var prodList = getProductsAsync("3rd party service URL and parameters");
var prodHtml = '';
for(var i = 0; i < prodList.length; i++){
prodHtml += `
<div class="prod">
<h3>${prodList[i].title}</h3>
<p>${prodList[i].price} €</p>
</div>
`;
}
Here I have 2 templates (one as JS html, and another in Vue.js component), that is redundant.
I need to have 1 way to maintain this template. At the moment template is simple, but more functionality will shortly come.
Idealy I would like my JS object to use my Vue.js template. As a result it would also behave as Vue.js component too.
So in my JS I would like to have it like this (But this obviously does not work):
var prodList = getProductsAsync("3rd party service URL and parameters");
var prodHtml = '';
for(var i = 0; i < prodList.length; i++){
prodHtml += ` <product :product='prodList[i]'></product>`; // attempt to use Vue.js component 'on the fly' but this does not work!
}
Any suggestions?
It doesn't work, because there is some order in which you have to create component, html, and Vue instance.
// just use all your code together
// first, prepare the component
Vue.component('product', {
props: ['product'],
template: `
<div class="prod">
<h3>{{product.title}}</h3>
<p>{{product.price}} €</p>
</div>
`
})
// and get products
var prodList = getProductsAsync('/someMountpoint')
// then generate html with unknown tags "product"
var prodHtml = '<div id="productList">'
for(let i = 0; i < prodList.length; i++) {
prodHtml += `<product :product='prodList[i]'></product>`
}
prodHtml += '</div>'
// append this html to DOM
$('#someWhere').html(prodHtml)
// and now create new Vue instance to replace
// these unknown product tags with real tags
new Vue({
el: '#productList'
})

Array is returning correct results. but shows undefined in HTML display

I am having an issue with a project I'm working on. I am using an API to return an array of platforms a video game is on. The array is returning the correct results, however I am having trouble displaying those values in my HTML. The result is just showing undefined.
// Renders platform information for each game
const renderPlatformInfo = function (platformResult) {
const gamePlatform = [];
for (let i = 0; i <= `${platformResult.length - 1}`; i++) {
let getPlatforms = gamePlatform.push(platformResult[i].name);
}
gamePlatform.forEach(function (platformItem) {
$('.platforms').append(`<li>${platformItem}</li>`)
});
console.log(gamePlatform);
};
// Renders game information for the searched game
const renderGameInfo = function (gameInfoResult) {
return `<div class="js-game-data row">
<h2 class="game-name col-12">${gameInfoResult.name}</h2>
<img src="${gameInfoResult.image.medium_url}" class="game-image col-4" alt="Box art for ${gameInfoResult.name}">
<ul class="platforms col-6">
<h3 class="col-12">Original release date:</h3>${gameInfoResult.original_release_date}
<h3>Platforms:</h3>
${renderPlatformInfo(gameInfoResult.platforms)}
</ul>
<p class="game-description col-6">${gameInfoResult.deck} <br> <br> <span class="game-details col-12"><b>For more details about the game: Click Here</b></span></p>
</div>
`;
}
renderPlatformInfo can't append children to a DOM element that doesn't exist yet. The UL it's trying to select isn't rendered at the time you're trying to append. Additionally, since renderPlatformInfo doesn't return anything, it will always evaluate to undefined inside a template literal. If you return an HTML string inside renderPlatformInfo, your code should work. Try something like:
let str = '';
gamePlatform.forEach(function(platformItem){
str += `<li>${platformItem}</li>`;
});
return str;
Should renderPlatformInfo return something. Are you missing the return there?

Protractor AngularJS count, copy, and verify a list span

I am new to automated testing, Protractor, and angularJS. I have a list that I would like to count, copy to an array maybe, and verify the list text is present. For example The list shows Attractions, Capacity, and Content to the user so they know what privileges they have.
Below is the .html
<div class="home-info">
<div class="home-top home-section">
<h3>User Information</h3>
<div class="home-box">
<div class="property-group wide">
<span>
Change Phillips<br />
</span>
</div>
</div>
<div class="home-box">
<div class="property-group wide">
<div>Editors:</div>
<span>
<ul class="property-stack">
<li><span>Attractions</span>
</li>
<li><span>Capacity</span>
</li>
<li><span>Content</span>
</li>
<li><span>Media</span>
</li>
<li><span>Options</span>
</li>
<li></li>
<li></li>
<li><span>Upload CADs</span>
</li>
</ul>
</span>
</div>
</div>
</div>
Below is the code I have written. I can get the first item on the list however using .all isn't working for me.
var text = "";
browser.driver.findElement.all(By.xpath("//li/span")).count().then(function(count) {
initialCount = count;
console.log(initialCount);
});
browser.driver.findElement(By.xpath("//li/span")).getText().then(function(text) {
console.log(text);
});
I'm trying to avoid using xpath as I was told to try and avoid. To be honest Im lost. Thanks for the help in advance.
Code used for matching:
expect(myLists).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
I am not sure what version of protractor you're using but you should be able to just call element without the browser or driver prefix. Using element.all should get you the array of of elements you're looking for.
If you want to access specific indexes within that array you can use the .get(index) suffix to the element.all
So below:
1. you get the array of the elements
2. you get the count of the array
3. we call a for loop to iterate through all the indexes of the array
4. each index of the array we call the getText() and print it to the console
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
for(int i = 0; i < count; i++){
myLists.get(i).getText().then(function(text) {
textList[j++] = text;
console.log(text);
});
}
});
EDIT:
In researching I actually found another way to iterate through the array of elements by using the .each() suffix to the element.all.
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
myLists.each(function(element, index) {
element.getText().then(function (text) {
textList[j++] = text;
console.log(index, text);
});
});
});
you should be able to use the textList array to match things.
expect(textList).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);

Categories

Resources