JSON to unordered list, sorted - javascript

I have an xml file that I made a JSON array with objects that are senators with a party, name, and a status of whether they have been voted or not. Only the name needs to be displayed on my HTML list. I don't know how to get it there, though, and I want to sort democrats and republicans dynamically as I go. here is a sample of the array:
[{"name":"Chuck Schumer","party":"Democrat","voted":false},
{"name":"Dick Durbin","party":"Democrat","voted":false}, ...]
I'm not sure how one does this. I have ID elements set up in my html because I know I need that.
Do I need to JSON.parse first? how do you connect them to the ID values?
Here is my HTML body.
<div id="dropLists" style="display: table">
<div style="display: table-row">
<div class="dropList">
<fieldset>
<legend>Democrats:</legend>
<ul id="democrats">
</ul>
</fieldset>
</div>
<div class="dropList">
<fieldset>
<legend>Republicans:</legend>
<ul id="republicans">
</ul>
</fieldset>
</div>
</div>
</div>

You're almost there. Here's an overview:
document.querySelector to select the 2 ul elements
document.createElement to create li elements
element.appendChild to insert the li elements into the `uls element.
let data = [{
"name": "Chuck Schumer",
"party": "Democrat",
"voted": false
},
{
"name": "Dick Durbin",
"party": "Democrat",
"voted": false
},
{
"name": "X Y Z",
"party": "Republican",
"voted": false
},
];
data.forEach(({name, party}) => {
let itemEl = document.createElement('li');
itemEl.textContent = name;
let listId = party === 'Democrat' ? '#democrats' : '#republicans';
let listEl = document.querySelector(listId);
listEl.appendChild(itemEl);
});
<div id="dropLists" style="display: table">
<div style="display: table-row">
<div class="dropList">
<fieldset>
<legend>Democrats:</legend>
<ul id="democrats">
</ul>
</fieldset>
</div>
<div class="dropList">
<fieldset>
<legend>Republicans:</legend>
<ul id="republicans">
</ul>
</fieldset>
</div>
</div>
</div>

Filter the list to get either "democrats" or "republicans", map the results to have only the name wrapped in a li and set the innerHTML of the coresponding ul ( html elements with id will be global variables, so you do democrats.innerHTML = ... )
const data = [{"name":"Chuck Schumer","party":"Democrat","voted":false},{"name":"Dick Durbin","party":"Democrat","voted":false},{"name":"Dick Durbin 2","party":"Republican","voted":false}]
democrats.innerHTML = data.filter(o => o.party === "Democrat").map(o => '<li>' + o.name + '</li>').join('');
republicans.innerHTML = data.filter(o => o.party === "Republican").map(o => '<li>' + o.name + '</li>').join('');
<div id="dropLists" style="display: table">
<div style="display: table-row">
<div class="dropList">
<fieldset>
<legend>Democrats:</legend>
<ul id="democrats">
</ul>
</fieldset>
</div>
<div class="dropList">
<fieldset>
<legend>Republicans:</legend>
<ul id="republicans">
</ul>
</fieldset>
</div>
</div>
</div>

Where is the code getting the data from? Is it reading an XML file? Calling a URL and downloading it? Or are you copying/pasting your array into the code? If you're copying/pasting it, then it's already an array and doesn't need to be JSON.parsed. If it's coming as the entire contents of a file obtained via fetch, then you can just use response.json() on the result of the fetch to convert it into an array. Any other source would likely give it to you as a string, in which case you just need to call JSON.parse() on the string to get the array.
Once you have an array, it's extremely easy to split it into two lists, one for each party:
const dems = senators.filter(senator => senator.party === 'Democrat');
const reps = senators.filter(senator => senator.party === 'Republican');
And then to convert them to an HTML list inside your existing ul elements:
const demList = document.getElementById('democrats');
dems.forEach(dem => {
const listItem = document.createElement('li');
listItem.textContent = dem.name;
demList.appendChild(listItem);
});
(The code is similar for the Republican list.)

Related

Javascript - Use array values dynamically in HTML

My end result is supposed to be a list of objects in html. Bootstrap behind this. I'd like for the list to be created dynamically so I don't have to manually create all the divs because I don't know how many there will be. Here's what I have.
I have an array similar to this:
activities =
[
{
"activityOwner": "Raymond Carlson",
"activityDesc": "Complete all the steps from Getting Started wizard"
},
{
"activityOwner": "Flopsy McDoogal",
"activityDesc": "Called interested in March fundraising Sponsorship"
},
{
"activityOwner": "Gary Busy",
"activityDesc": "Get approval for price quote"
}
]
This is the first part where I'm not sure what to do. I can assign the element ids individually for my html like this but what I'd like to do is count how many elements are in my array and create these for me. I won't know how many there are to make these manually. I'm sure there needs to be a loop but I couldn't figure it out.
document.getElementById('activityowner0').innerHTML = activities[0].activityOwner;
document.getElementById('activitydesc0').innerHTML = activities[0].activityDesc;
document.getElementById('activityowner1').innerHTML = activities[1].activityOwner;
document.getElementById('activitydesc1').innerHTML = activities[1].activityDesc;
document.getElementById('activityowner2').innerHTML = activities[2].activityOwner;
document.getElementById('activitydesc2').innerHTML = activities[2].activityDesc;
etc.
etc.
And then...once I have that part, I'd like to know how to create my html divs dynamically based on how many elements are in my array. Again, right now I don't know how many there are so I'm having to create a bunch of these and then have extras if I have too many.
<div class="container">
<div class="row"></div>
<div class="qa-message-list" id="wallmessages">
<br>
<div class="message-item" id="m0">
<div class="message-inner">
<div class="message-head clearfix">
<div class="user-detail">
<h5 class="handle">
<p id='activityowner0'></p>
</h5>
<div class="post-meta"></div>
</div>
</div>
<div class="qa-message-content">
<p id='activitydesc0'></p>
</div>
</div>
</div>
I know this is a big ask so just pointing me in the right direction would be very helpful. I hope my question was clear and I appreciate it.
One way for you to achieve this would be to loop through the objects in your activities array. From there you can use a HTML template to store the base HTML structure which you can clone and update with the values of each object before you append it to the DOM.
In addition, an important thing to note when generating repeated content in a loop: never use id attributes. You will either end up with duplicates, which is invalid as id need to be unique, or you'll end up with ugly code generating incremental/random id at runtime which is unnecessary. Use classes instead.
Here's a working example:
const activities = [{ "activityOwner": "Raymond Carlson", "activityDesc": "Complete all the steps from Getting Started wizard"}, {"activityOwner": "Flopsy McDoogal","activityDesc": "Called interested in March fundraising Sponsorship" }, { "activityOwner": "Gary Busy", "activityDesc": "Get approval for price quote" }]
const html = activities.map(obj => {
let item = document.querySelector('#template').innerHTML;
item = item.replace('{owner}', obj.activityOwner);
item = item.replace('{desc}', obj.activityDesc);
return item;
});
document.querySelector('#list').innerHTML = html.join('');
<div id="list"></div>
<template id="template">
<div class="container">
<div class="row"></div>
<div class="qa-message-list">
<div class="message-item">
<div class="message-inner">
<div class="message-head clearfix">
<div class="user-detail">
<h5 class="handle">
<p class="activityowner">{owner}</p>
</h5>
<div class="post-meta"></div>
</div>
</div>
<div class="qa-message-content">
<p class="activitydesc">{desc}</p>
</div>
</div>
</div>
</div>
</div>
</template>

How to filter this list of div elements with the keyup event

I want to filter with the keyup event, on the input html element: when the user enters something, I want to show just the div elements that contain that name
of "the protein".
The div with class "elementsName" is the element which contains those protein names; .theMainSectionInTheModalBody is a div element.
I know that it is much easier with list elements, but I must use div, because positioning my elements (image, text, etc) in just a list element is hard.
proteins is just an array of objects, where all elements are inputed.
const theLookOfTheProteinsInTheModalBody = () =>
{
for(let elements of proteins) {
$('.theMainSectionInTheModalBody').append
(`
<div class="calculatorBox">
<span class="openTheDescriptionText">+</span>
<div class="elementsName">${ elements.name } <span>${ elements.price }</span></div>
<span><img class="small" src=${elements.image} /></span>
</div>
<div class="panel">
<p class="calculatorBoxText">${elements.text}</p>
</div>
`)
}
}
Here is the HTML code:
<div class="modal-header">
<span class="closeTheModalBox">×</span>
<h3>Your current sum from the shopping</h3>
<input type="text" id="filter" />
<button class="sortByHighestPrices">Sort the prortein by the highest price</button>
<button class="sortByLowestPrices">Sort the prortein by the lowest price</button>
<div id="sum"></div>
</div>
<div class="modal-body">
<div class="theMainSectionInTheModalBody"></div>
</div>
I would suggest to perform the filtering and sorting on an array, and not directly on the DOM elements. With minor modification, your existing function can be used to re-populate the list based on a filtered/sorted array:
It should take the array as argument
It should clear the DOM content before populating
Better use the input event than the keyup event. Also bind click handlers to your buttons, and let those buttons first sort the original proteins array before calling a common refresh function. That latter function should then apply the filter and pass the filtered array to the function you have (with above changes):
const theLookOfTheProteinsInTheModalBody = (proteins) => { // pass the argument
$('.theMainSectionInTheModalBody').empty(); // <-- clear list before populating
for(let elements of proteins) {
$('.theMainSectionInTheModalBody').append(`
<div class="calculatorBox">
<span class="openTheDescriptionText">+<\/span>
<div class="elementsName">${ elements.name } <span>${ elements.price }<\/span><\/div>
<span><img class="small" src="${elements.image}" /><\/span>
<\/div>
<div class="panel">
<p class="calculatorBoxText">${elements.text}<\/p>
<\/div>
`);
}
};
// Sample data
var proteins = [{
name: "banana",
price: 12.33,
image: "test.img",
text: "they are yellow"
}, {
name: "apple",
price: 9.25,
image: "test.img",
text: "one apple a day..."
}];
function refresh() {
const find = $("#filter").val();
// Only pass filtered list to display function:
theLookOfTheProteinsInTheModalBody(proteins.filter(elem => elem.name.includes(find)));
}
// Use input event (not keyup)
$("#filter").on("input", refresh);
$(".sortByHighestPrices").click(() => {
proteins.sort((a,b) => b.price - a.price);
refresh();
});
$(".sortByLowestPrices").click(() => {
proteins.sort((a,b) => a.price - b.price);
refresh();
});
refresh();
.elementsName{ display: inline-block }
.small { width: 20px; height: 20px }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="modal-header">
<span class="closeTheModalBox">×</span>
<h3>Your current sum from the shopping</h3>
<input type="text" id="filter" />
<button class="sortByHighestPrices">Sort the prortein by the highest price</button>
<button class="sortByLowestPrices">Sort the prortein by the lowest price</button>
<div id="sum"></div>
</div>
<div class="modal-body">
<div class="theMainSectionInTheModalBody"></div>
</div>

rendering list of objects in react/mobx

i have a list of objects in my jsx class. Let's assume it's a fixed list of objects for now
versions = [
{owner: "luca", date: "today", fw: "00"},
{owner: "thomas", date: "tomorrow", fw: "00"},
{owner: "peter", date: "yesterday", fW: "00"},];
i'm trying to render the values of these objects in nested div elements on my webpage. basically it's a panel of rows that i represent as divs. here's the html for it
<div className="fc-revisions-sidebar revisions-panel flex-vertical flex-grow-1">
<div className="fc-revisions-sidebar-header fc-revisions-sidebar-header-bg-color-brand">
<div className="fc-revisions-sidebar-title">Version history</div>
</div>
<div className="fc-revisions-sidebar-revisions-list-container">
<div className="fc-revisions-sidebar-revisions-list">
<div role="rowgroup">
<div className="fc-revisions-collapsible-panel" role="button">
<div className="fc-revisions-collapsible-panel-container">
<div className="fc-revisions-row fc-revisions-row-selected" role="row" aria-selected="true" aria-level="1">
<div className="fc-revisions-row-content-wrapper">
<div className="fc-revisions-row-header fc-row-content">
<div className="fc-revisions-row-text-box" rows="1" maxLength="80" aria-multiline="false">
**{version.date}**
</div>
</div>
<div className="fc-revisions-row-content fc-row-content" role="presentation">
<div className="fc-revisions-row-collaborator-list">
<div className="fc-revisions-row-collaborator">
<span className="fc-versions-rown-collaborators-label">Created by **{version.owner}**</span>
<span className="fc-revisions-row-collaborator-name">**{version.fw}**</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
i'm not sure how to implement this in my component class!!
starting from the first div after this one
<div role="rowgroup">
my html code to create each row in the panel starts.
I want to iterate over the objects in my list and create/fill each row in my panel with the right data from that list
I've tried a dozen different ways but nothing is showing up on my webpage. I just don't understand how to iterate over the list of objects in 'versions' and create/fill the panel in progress.
Let assume you have array of objects declared inside render using const. You can iterate the array either using .map, .forEach, for loop etc. In your case I would prefer .map for iteration because map returns new array. So inside the map construct jsx elements and return them.
Now, returned jsx elements will be placed in versionItems array. You can just call that with {} like expression in render return.
render(){
const versions = [
{owner: "luca", date: "today", fw: "00"},
{owner: "thomas", date: "tomorrow", fw: "00"},
{owner: "peter", date: "yesterday", fW: "00"},];
const versionItems = versions.map((item, index) => {
return (
<div key={"key"+index} role="rowgroup">
//just get all your property values here using item.owner, item.date etc
</div>
)
});
return(
<div>
{versionItems}
</div>
)
}
Iteration is normally done by maping an array of values to an array of components. Something like this:
versions = [ ... ]
return (
<div>
<div>Version History</div>
{
versions.map(version =>
<div key={version.date}>
{version.date}
</div>
)
}
</div>
)
Note that for Reacts reconciliation to work properly when potentially re-rendering with a new array of values, the outer element in the array should have a unique key attribute so that React quickly can recognize any removed or added values in the array on the next render.

Adding complex HTML dynamically to div using Javascript

I have a webpage that list a lot of elements (movies to be specific), the HTML structure of every item is in some way large and complicated (divs, images, links, CSS class, etc).
Firstly I load 100 elements and the user have the option of load the next 100 (this is made using infinite scroll): by now, I make a AJAX petition requesting the another 100 elements and it responds with a HTML text (with all of them loaded) and I just append it to the document.
But, now I don't want to respond with the HTML text, instead of that I want to respond with the 100 elements data in a JSON (I can do that), then, my question is: Which is the best way to add these elements to the document using Javascript?
I know that I can loop over the JSON array and construct every element, but as I said, it's a large HTML structure and I don't really want to create divs and then attach it to another div,set CSS classes, etc with Javascript, because it might get disordered,messy and very large...So, there's a way in javascript to achieve this maybe using something like templates? How can I do that? I just want to get a clean and better code.
The structure of every movie is like this (can I use it like a template?):
<div data-section="movies" data-movie_id="myid" id="movie-id" class="movie anotherclass">
<img src="myImageUrl">
<div class="aCSSclass">
<div class="aCSSclass">
<div class="aCSSclass"></div>
<div class="aCSSclass">
<div class="aCSSclass">
Movie title
</div>
<div class="details form-group">
<a class="aCSSclass" href="myHref">Details</a>
<button onclick="SomeFunction" class="aCSSclass">My button</button>
<div class="aCSSclass"><span class="icon star"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span></div>
</div>
</div>
</div>
</div>
</div>
The answer is to make a template and then copy the node using cloneNode(). Append all the cloned nodes to a documentFragment to save time on drawing and finally append it to the page.
An approach to this:
var movies = {"movie1" : { "title" : "Die Hard", "imageurl" : "example.com/image.jpg", "details" : "http://example.com", "func" : "functionname" },
"movie2" : { "title" : "Die Hard 2", "imageurl" : "example.com/image.jpg", "details" : "http://example.com", "func" : "functionname" },
"movie3" : { "title" : "Die Hard With 3", "imageurl" : "example.com/image.jpg", "details" : "http://example.com", "func" : "functionname" }
};
function functionname()
{
alert("NYI");
}
var keys = Object.keys(movies); //get the keys.
var docFrag = document.createDocumentFragment();
for (var i = 0; i < keys.length; i++)
{
var tempNode = document.querySelector("div[data-type='template']").cloneNode(true); //true for deep clone
tempNode.querySelector("div.title").textContent = movies[keys[i]].title;
tempNode.querySelector("img").src = movies[keys[i]].imageurl;
tempNode.querySelector("button").onclick = window[movies[keys[i]].func];
tempNode.querySelector("a").href = movies[keys[i]].details;
tempNode.style.display = "block";
docFrag.appendChild(tempNode);
}
document.body.appendChild(docFrag);
delete docFrag;
<!-- template -->
<div style="display: none" data-type="template" data-section="movies" data-movie_id="myid" id="movie-id" class="movie anotherclass">
<img src="myImageUrl">
<div class="aCSSclass">
<div class="aCSSclass">
<div class="aCSSclass"></div>
<div class="aCSSclass">
<div class="aCSSclass title">
Movie title
</div>
<div class="details form-group">
<a class="aCSSclass" href="myHref">Details</a>
<button onclick="SomeFunction" class="aCSSclass">My button</button>
<div class="aCSSclass"><span class="icon star"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span></div>
</div>
</div>
</div>
</div>
</div>
This is just an example, not based upon your actual JSON. However you can easily clone a template and then fill in the values.
Use
document.querySelector
document.querySelectorAll
document.createDocumentFragment
Element.cloneNode(bool)

Custom dynamic nested ul list from a json using a JS for loop

Hello I am trying to render a dynamic nested ul list from a json using a for loop in javascript,
After trying for couple of hours I was able to make it work,there are some small details i need to fix, for example my javascript is rendering all the objects as html elements, how do I render only the elements needed on the view, for example:
<ul class="some_class">
<li>
<h3>
level_0_name => Africa
<input type="checkbox" name="level_0_id => VALUE HERE" id="">
</h3>
<ul class="some_class">
<li>
<h3>
level_1_name => Company San Fracisco
<input type="checkbox" name="level_1_id => VALUE HERE" id="">
</h3>
<ul class="some_class">
<li>
<h3>
level_2_name => Some title
<input type="checkbox" name="level_2_id => VALUE HERE" id="">
</h3>
<ul class="some_class">
<li>
<h3>
John Doe
<input type="checkbox" name="" id="" value="id VALUE">
</h3>
</li>
if you look at the http://jsfiddle.net/creativestudio/3avzp6o3/3/ i created you will notice the menu starts at:
"level_0": [
and is rendering markup not needed, is there an easy way to only render the objects with an actual value like "level_1_id" => "12345" into the list?
This is the JS function:
function tree(data) {
if (typeof(data) == 'object') {
var ul = $('<ul>');
for (var i in data) {
ul.append($('<li>').text(i).append(tree(data[i])));
}
return ul;
} else {
var textNode = document.createTextNode(' => ' + data);
return textNode;
}
}
$(document.body).append(tree(data));

Categories

Resources